//
// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

#include <math.h>

#include <algorithm>
#include <array>
#include <cmath>
#include <cstdint>
#include <initializer_list>
#include <iterator>
#include <limits>
#include <numeric>
#include <optional>
#include <set>
#include <string>
#include <utility>
#include <vector>

#include "zetasql/base/logging.h"
#include "google/protobuf/wrappers.pb.h"
#include "zetasql/compliance/functions_testlib_common.h"
#include "zetasql/public/civil_time.h"
#include "zetasql/public/functions/bitwise_agg_mode.pb.h"
#include "zetasql/public/functions/date_time_util.h"
#include "zetasql/public/interval_value.h"
#include "zetasql/public/json_value.h"
#include "zetasql/public/numeric_value.h"
#include "zetasql/public/options.pb.h"
#include "zetasql/public/type.h"
#include "zetasql/public/type.pb.h"
#include "zetasql/public/types/type_factory.h"
#include "zetasql/public/uuid_value.h"
#include "zetasql/public/value.h"
#include "zetasql/testing/test_function.h"
#include "zetasql/testing/test_value.h"
#include "zetasql/testing/using_test_value.cc"  // NOLINT
#include "gtest/gtest.h"
#include "absl/container/btree_map.h"
#include "absl/status/status.h"
#include "absl/strings/cord.h"
#include "absl/strings/string_view.h"
#include "absl/time/time.h"
#include "absl/types/span.h"

namespace zetasql {
namespace {
constexpr absl::StatusCode OUT_OF_RANGE = absl::StatusCode::kOutOfRange;
constexpr absl::StatusCode OK = absl::StatusCode::kOk;
}  // namespace

std::vector<QueryParamsWithResult> GetFunctionTestsAnd() {
  return {
    // Binary.
    {{False(), False()}, False()},
    {{False(), True()}, False()},
    {{True(), False()}, False()},
    {{True(), True()}, True()},
    {{True(), NullBool()}, NullBool()},
    {{NullBool(), True()}, NullBool()},
    {{False(), NullBool()}, False()},
    {{NullBool(), False()}, False()},
    {{NullBool(), NullBool()}, NullBool()},
    // Ternary. The third argument affects the result.
    {{True(), True(), NullBool()}, NullBool()},
    {{True(), True(), False()}, False()},
    {{True(), NullBool(), False()}, False()}
  };
}

std::vector<QueryParamsWithResult> GetFunctionTestsOr() {
  return {
    // Binary.
    {{False(), False()}, False()},
    {{False(), True()}, True()},
    {{True(), False()}, True()},
    {{True(), True()}, True()},
    {{True(), NullBool()}, True()},
    {{NullBool(), True()}, True()},
    {{False(), NullBool()}, NullBool()},
    {{NullBool(), False()}, NullBool()},
    {{NullBool(), NullBool()}, NullBool()},
    // Ternary. The third argument affects the result.
    {{False(), False(), True()}, True()},
    {{False(), False(), NullBool()}, NullBool()},
    {{False(), NullBool(), True()}, True()}
  };
}

std::vector<QueryParamsWithResult> GetFunctionTestsNot() {
  return {
    {{False()}, True()},
    {{True()}, False()},
    {{NullBool()}, NullBool()}
  };
}

// Note that many array tests are generated by taking non-array tests
// and wrapping the values in arrays for the comparison.  This function
// returns test cases that aren't covered by those tests, such as
// NULL array values, arrays with different lengths, arrays with elements
// that are NULLs and NaNs, etc.  So for instance, these tests use
// ARRAY<DOUBLE> in order to test NaN behavior, and arrays with other
// element types are not covered here.
//
// The test result can be UNEQUAL, which in this case indicates that the
// two arrays compares as NOT EQUAL, but it cannot be determined if one
// array is LESS than the other.  For example, consider the comparison between:
//
// [NULL]
// [1, 2]
//
// We know these arrays are NOT EQUAL since they have different lengths.  But
// LESS comparison returns NULL because the comparison between the first
// element in the two arrays results in NULL.
//
// TODO: We need to add nested arrays cases as well, but after we
//     hammer down the exact details and semantics of all corner cases.
static std::vector<ComparisonTest> GetArrayComparisonTests() {
  const Value null_array = Value::Null(DoubleArrayType());
  const Value empty_array = Value::EmptyArray(DoubleArrayType());

  const Value singleton_null =
      values::Array(DoubleArrayType(), {Value::NullDouble()});
  const Value singleton_zero = values::DoubleArray({0});
  const Value singleton_one = values::DoubleArray({1});
  const Value one_null =
      values::Array(DoubleArrayType(), {Value::Double(1), Value::NullDouble()});
  const Value one_zero = values::DoubleArray({1, 0});
  const Value one_one = values::DoubleArray({1, 1});
  const Value singleton_two = values::DoubleArray({2});

  const Value one_null_zero =
      values::Array(DoubleArrayType(),
                    {Value::Double(1), Value::NullDouble(), Value::Double(0)});
  const Value one_null_one =
      values::Array(DoubleArrayType(),
                    {Value::Double(1), Value::NullDouble(), Value::Double(1)});
  const Value one_one_one = values::DoubleArray({1, 1, 1});

  const Value singleton_nan = values::DoubleArray({double_nan});
  const Value zero_nan = values::DoubleArray({0, double_nan});
  const Value one_nan = values::DoubleArray({1, double_nan});
  const Value one_nan_zero = values::DoubleArray({1, double_nan, 0});
  const Value one_nan_one = values::DoubleArray({1, double_nan, 1});

  const Value singleton_pos_inf = values::DoubleArray({double_pos_inf});
  const Value singleton_neg_inf = values::DoubleArray({double_neg_inf});

  std::vector<ComparisonTest> v = {
      // same null array, compare as NULL for both equality and non-equality.
      {null_array, null_array, NULL_VALUE},

      // same non-null array
      {empty_array, empty_array, EQUAL},
      {singleton_zero, singleton_zero, EQUAL},
      {one_zero, one_zero, EQUAL},

      // same array with null elements
      {singleton_null, singleton_null, NULL_VALUE},
      {one_null, one_null, NULL_VALUE},

      // null array and non-null array
      {null_array, empty_array, NULL_VALUE},
      {null_array, singleton_null, NULL_VALUE},
      {null_array, singleton_nan, NULL_VALUE},
      {null_array, singleton_neg_inf, NULL_VALUE},
      {null_array, singleton_zero, NULL_VALUE},
      {null_array, one_null, NULL_VALUE},

      // empty array an non-empty array
      {empty_array, singleton_null, LESS},
      {empty_array, singleton_nan, LESS},
      {empty_array, singleton_neg_inf, LESS},
      {empty_array, singleton_zero, LESS},
      {empty_array, singleton_one, LESS},
      {empty_array, one_null, LESS},
      {empty_array, one_zero, LESS},
      {empty_array, one_one, LESS},
      {empty_array, singleton_two, LESS},

      // An array with the first NULL element compares as NULL with all
      // other arrays with at least one element.
      {singleton_null, singleton_neg_inf, NULL_VALUE},
      {singleton_null, singleton_zero, NULL_VALUE},
      {singleton_null, singleton_one, NULL_VALUE},
      // We do know these are not equal because array lengths are not equal, but
      // for LESS/GREATER the result is NULL.
      {singleton_null, zero_nan, ARRAY_UNEQUAL_ORDERS_LESS},
      {singleton_null, one_null, ARRAY_UNEQUAL_ORDERS_LESS},
      {singleton_null, one_nan, ARRAY_UNEQUAL_ORDERS_LESS},
      {singleton_null, one_zero, ARRAY_UNEQUAL_ORDERS_LESS},
      {singleton_null, one_one, ARRAY_UNEQUAL_ORDERS_LESS},
      {singleton_null, one_nan_zero, ARRAY_UNEQUAL_ORDERS_LESS},

      {singleton_zero, singleton_one, LESS},
      {singleton_zero, one_null, LESS},
      {singleton_zero, one_zero, LESS},
      {singleton_zero, one_one, LESS},
      {singleton_zero, singleton_two, LESS},

      {singleton_one, one_null, LESS},
      {singleton_one, one_zero, LESS},
      {singleton_one, one_one, LESS},
      {singleton_one, singleton_two, LESS},

      {one_null, one_zero, NULL_VALUE},
      {one_null, one_one, NULL_VALUE},
      {one_null, singleton_two, LESS},
      {one_null, one_null_zero, ARRAY_UNEQUAL_ORDERS_LESS},
      {one_null, one_null_one, ARRAY_UNEQUAL_ORDERS_LESS},
      {one_null, one_one_one, ARRAY_UNEQUAL_ORDERS_LESS},

      {one_zero, one_one, LESS},
      {one_zero, singleton_two, LESS},

      {one_one, singleton_two, LESS},

      {one_null_zero, one_null_one, ARRAY_UNEQUAL_ORDERS_LESS},
      {one_null_zero, one_one_one, ARRAY_UNEQUAL_ORDERS_LESS},

      {one_null_one, one_one_one, NULL_VALUE},

      // Comparing NaN to a NULL returns NULL, comparing NaN to non-NULL
      // returns FALSE (including comparing NaN to NaN).
      {singleton_nan, singleton_null, NULL_VALUE},
      {singleton_nan, one_null, UNORDERED_BUT_ARRAY_ORDERS_LESS},
      {singleton_nan, singleton_two, UNORDERED_BUT_ARRAY_ORDERS_LESS},
      {singleton_nan, singleton_nan, UNORDERED_BUT_ARRAY_ORDERS_LESS},
      {singleton_nan, zero_nan, UNORDERED_BUT_ARRAY_ORDERS_LESS},
      {singleton_nan, one_nan, UNORDERED_BUT_ARRAY_ORDERS_LESS},
      {singleton_nan, one_nan_zero, UNORDERED_BUT_ARRAY_ORDERS_LESS},
      {singleton_nan, one_nan_one, UNORDERED_BUT_ARRAY_ORDERS_LESS},
      {singleton_nan, singleton_pos_inf, UNORDERED_BUT_ARRAY_ORDERS_LESS},
      {singleton_nan, singleton_neg_inf, UNORDERED_BUT_ARRAY_ORDERS_LESS},

      {zero_nan, one_null, LESS},
      {zero_nan, singleton_two, LESS},
      {zero_nan, zero_nan, UNORDERED_BUT_ARRAY_ORDERS_LESS},
      {zero_nan, one_nan, LESS},
      {zero_nan, one_nan_zero, LESS},
      {zero_nan, one_nan_one, LESS},

      {one_nan, one_null, NULL_VALUE},
      {one_nan, singleton_two, LESS},
      {one_nan, one_nan, UNORDERED_BUT_ARRAY_ORDERS_LESS},
      {one_nan, one_nan_zero, UNORDERED_BUT_ARRAY_ORDERS_LESS},
      {one_nan, one_nan_one, UNORDERED_BUT_ARRAY_ORDERS_LESS},

      {one_null, one_nan_zero, ARRAY_UNEQUAL_ORDERS_LESS},
      {one_nan_zero, singleton_two, LESS},
      {one_nan_zero, one_nan_zero, UNORDERED_BUT_ARRAY_ORDERS_LESS},
      {one_nan_zero, one_nan_one, UNORDERED_BUT_ARRAY_ORDERS_LESS},

      {singleton_pos_inf, null_array, NULL_VALUE},
      {singleton_pos_inf, singleton_null, NULL_VALUE},
  };

  return v;
}

// Only struct equality/inequality is defined for ZetaSQL, not other
// comparisons (less than, etc.).  Therefore all tests should be defined
// as EQUAL, UNORDERED (not equal), or NULL_VALUE.
static std::vector<ComparisonTest> GetStructComparisonTests() {
  const StructType* struct_type = SimpleStructType();  // a: string, b: int32
  const Value struct0 = Value::Struct(struct_type, {String("foo"), Int32(0)});
  const Value struct1 = Value::Struct(struct_type, {String("bar"), Int32(1)});
  const Value struct_with_null2 =
      Value::Struct(struct_type, {NullString(), Int32(1)});
  const Value struct_with_null3 =
      Value::Struct(struct_type, {String("bar"), NullInt32()});
  const Value struct_with_null4 =
      Value::Struct(struct_type, {NullString(), NullInt32()});
  const Value null_struct = Value::Null(struct_type);

  // a: string, b: {a: string b: int32}
  const StructType* nested_struct_type;
  ZETASQL_CHECK_OK(type_factory()->MakeStructType(
      {{"a", StringType()}, {"b", struct_type}}, &nested_struct_type));
  const Value null_nested_struct = Value::Null(nested_struct_type);
  const Value nested_struct1 =
      Value::Struct(nested_struct_type, {String("x"), struct1});
  const Value nested_struct1b =
      Value::Struct(nested_struct_type, {String("x"), struct1});
  const Value nested_struct_null1 =
      Value::Struct(nested_struct_type, {NullString(), struct1});
  const Value nested_struct_null2 =
      Value::Struct(nested_struct_type, {String("x"), struct_with_null2});
  const Value nested_struct_null3 =
      Value::Struct(nested_struct_type, {String("x"), struct_with_null3});
  const Value nested_struct_null4 =
      Value::Struct(nested_struct_type,
        {String("x"), Value::Null(struct_type)});

  // These DCHECKs are only here to more clearly show the contents of
  // all the struct values.
  ABSL_DCHECK_EQ("(\"foo\", 0)",    struct0.GetSQLLiteral());
  ABSL_DCHECK_EQ("(\"bar\", 1)",    struct1.GetSQLLiteral());
  ABSL_DCHECK_EQ("(NULL, 1)",       struct_with_null2.GetSQLLiteral());
  ABSL_DCHECK_EQ("(\"bar\", NULL)", struct_with_null3.GetSQLLiteral());
  ABSL_DCHECK_EQ("(NULL, NULL)",    struct_with_null4.GetSQLLiteral());
  ABSL_DCHECK_EQ("NULL",            null_struct.GetSQLLiteral());

  ABSL_DCHECK_EQ("NULL",                     null_nested_struct.GetSQLLiteral());
  ABSL_DCHECK_EQ("(\"x\", (\"bar\", 1))",    nested_struct1.GetSQLLiteral());
  ABSL_DCHECK_EQ("(\"x\", (\"bar\", 1))",    nested_struct1b.GetSQLLiteral());
  ABSL_DCHECK_EQ("(NULL, (\"bar\", 1))",     nested_struct_null1.GetSQLLiteral());
  ABSL_DCHECK_EQ("(\"x\", (NULL, 1))",       nested_struct_null2.GetSQLLiteral());
  ABSL_DCHECK_EQ("(\"x\", (\"bar\", NULL))", nested_struct_null3.GetSQLLiteral());
  ABSL_DCHECK_EQ("(\"x\", NULL)",            nested_struct_null4.GetSQLLiteral());

  std::vector<ComparisonTest> v = {
      // same non-null struct
      {struct0, struct0, EQUAL},  // (foo, 0) vs. (foo, 0)
      {struct1, struct1, EQUAL},  // (bar, 1) vs. (bar, 1)

      // different non-null struct
      {struct0, struct1,
       UNORDERED_BUT_ARRAY_ORDERS_LESS},  // (foo, 0) vs. (bar, 1)

      // null struct and non-null struct
      {null_struct, struct1, NULL_VALUE},  // NULL vs. (bar, 1)

      // non-null struct vs. struct with a null field
      {struct0, struct_with_null2,
       UNORDERED_BUT_ARRAY_ORDERS_LESS},         // (foo, 0) vs. (NULL, 1)
      {struct1, struct_with_null2, NULL_VALUE},  // (bar, 1) vs. (NULL, 1)
      {struct0, struct_with_null3,
       UNORDERED_BUT_ARRAY_ORDERS_LESS},         // (foo, 0) vs. (bar, NULL)
      {struct1, struct_with_null3, NULL_VALUE},  // (bar, 1) vs. (bar, NULL)
      {struct0, struct_with_null4, NULL_VALUE},  // (foo, 0) vs. (NULL, NULL)
      {struct1, struct_with_null4, NULL_VALUE},  // (bar, 1) vs. (NULL, NULL)

      // null struct vs. struct with null field
      {null_struct, struct_with_null2, NULL_VALUE},  // NULL vs. (NULL, 1)
      {null_struct, struct_with_null3, NULL_VALUE},  // NULL vs. (bar, NULL)
      {null_struct, struct_with_null4, NULL_VALUE},  // NULL vs. (NULL, NULL)

      // null struct vs. same null struct
      {null_struct, null_struct, NULL_VALUE},  // NULL vs. NULL

      // Nested structs
      {nested_struct1, nested_struct1,
       EQUAL},  // (x, (bar, 1)) v. (x, (bar, 1))
      {nested_struct1, nested_struct1b,
       EQUAL},  // (x, (bar, 1)) v. (x, (bar, 1))

      // (x, (bar, 1)) vs. NULL
      {nested_struct1, null_nested_struct, NULL_VALUE},

      // (x, (bar, 1)) vs. struct with null
      {nested_struct1, nested_struct_null1, NULL_VALUE},  // (NULL, (bar, 1))
      {nested_struct1, nested_struct_null2, NULL_VALUE},  // (x, (NULL, 1))
      {nested_struct1, nested_struct_null3, NULL_VALUE},  // (x, (bar, NULL))
      {nested_struct1, nested_struct_null4, NULL_VALUE},  // (x, NULL)

      // struct with null vs. same struct with null
      {null_nested_struct, null_nested_struct, NULL_VALUE},
      {nested_struct_null1, nested_struct_null1, NULL_VALUE},
      {nested_struct_null2, nested_struct_null2, NULL_VALUE},
      {nested_struct_null3, nested_struct_null3, NULL_VALUE},
      {nested_struct_null4, nested_struct_null4, NULL_VALUE},

      // struct with null vs. different struct with null
      {null_nested_struct, nested_struct_null1, NULL_VALUE},
      {nested_struct_null1, nested_struct_null2, NULL_VALUE},
      {nested_struct_null2, nested_struct_null3, NULL_VALUE},
      {nested_struct_null3, nested_struct_null4, NULL_VALUE},
      {nested_struct_null4, null_nested_struct, NULL_VALUE},
  };
  return v;
}

static std::vector<ComparisonTest> GetComparisonTests(
    bool include_struct_comparisons, bool include_array_comparisons) {
  const Value enum0 = Value::Enum(TestEnumType(), 0);
  const Value enum1 = Value::Enum(TestEnumType(), 1);
  std::vector<ComparisonTest> v = {
      // bool
      {True(), True(), EQUAL},
      {False(), False(), EQUAL},
      {False(), True(), LESS},
      {True(), NullBool(), NULL_VALUE},

      // double
      {3.14, 3.14, EQUAL},
      {2.72, 3.14, LESS},
      {-3.14, -2.72, LESS},
      {-2.72, 3.14, LESS},
      {3.14, NullDouble(), NULL_VALUE},
      {NullDouble(), 3.14, NULL_VALUE},
      {0.0, 0.0, EQUAL},
      {0.0, double_pos_inf, LESS},
      {double_neg_inf, 0.0, LESS},
      {double_neg_inf, double_pos_inf, LESS},
      {double_pos_inf, double_pos_inf, EQUAL},
      {double_nan, 0.0, UNORDERED_BUT_ARRAY_ORDERS_LESS},
      {double_nan, double_pos_inf, UNORDERED_BUT_ARRAY_ORDERS_LESS},
      {double_nan, double_neg_inf, UNORDERED_BUT_ARRAY_ORDERS_LESS},
      {double_nan, double_nan, UNORDERED_BUT_ARRAY_ORDERS_LESS},
      {double_nan, NullDouble(), NULL_VALUE},
      {NullDouble(), double_nan, NULL_VALUE},
      {double_neg_inf, NullDouble(), NULL_VALUE},
      {NullDouble(), double_neg_inf, NULL_VALUE},
      {double_pos_inf, NullDouble(), NULL_VALUE},
      {NullDouble(), double_pos_inf, NULL_VALUE},

      // float
      {3.14f, 3.14f, EQUAL},
      {2.72f, 3.14f, LESS},
      {-3.14f, -2.72f, LESS},
      {-2.72f, 3.14f, LESS},
      {3.14f, NullFloat(), NULL_VALUE},
      {NullFloat(), 3.14f, NULL_VALUE},
      {0.0f, 0.0f, EQUAL},
      {0.0f, float_pos_inf, LESS},
      {float_neg_inf, 0.0f, LESS},
      {float_neg_inf, float_pos_inf, LESS},
      {float_pos_inf, float_pos_inf, EQUAL},
      {float_nan, 0.0f, UNORDERED_BUT_ARRAY_ORDERS_LESS},
      {float_nan, float_pos_inf, UNORDERED_BUT_ARRAY_ORDERS_LESS},
      {float_nan, float_neg_inf, UNORDERED_BUT_ARRAY_ORDERS_LESS},
      {float_nan, float_nan, UNORDERED_BUT_ARRAY_ORDERS_LESS},
      {float_nan, NullFloat(), NULL_VALUE},
      {NullFloat(), float_nan, NULL_VALUE},
      {float_neg_inf, NullDouble(), NULL_VALUE},
      {NullDouble(), float_neg_inf, NULL_VALUE},
      {float_pos_inf, NullDouble(), NULL_VALUE},
      {NullDouble(), float_pos_inf, NULL_VALUE},

      // int32
      {0, int32max, LESS},
      {int32min, int32max, LESS},
      {1, 1, EQUAL},
      {1, 2, LESS},
      {-2, -1, LESS},
      {-1, 1, LESS},
      {1, NullInt32(), NULL_VALUE},

      // int64
      {0ll, int64max, LESS},
      {int64min, int64max, LESS},
      {1ll, 1ll, EQUAL},
      {1ll, 2ll, LESS},
      {-2ll, -1ll, LESS},
      {-1ll, 1ll, LESS},
      {1ll, NullInt64(), NULL_VALUE},

      // uint32
      {0u, uint32max, LESS},
      {0u, 1u, LESS},
      {0u, uint32max, LESS},
      {1u, 1u, EQUAL},
      {0u, 2u, LESS},
      {1u, NullUint32(), NULL_VALUE},

      // uint64
      {0ull, uint64max, LESS},
      {0ull, 1ull, LESS},
      {0ull, uint64max, LESS},
      {1ull, 1ull, EQUAL},
      {0ull, 2ull, LESS},
      {1ull, NullUint64(), NULL_VALUE},

      // int64 vs. uint64
      {0ll, 0ull, EQUAL},
      {int64max, Uint64(int64max), EQUAL},
      {0ll, Uint64(int64max), LESS},
      {-1ll, 0ull, LESS},
      {0ll, 1ull, LESS},
      {0ull, 1ll, LESS},
      {int64max, int64max_plus_one, LESS},
      {-1ll, int64max_plus_one, LESS},
      {-1ll, NullUint64(), NULL_VALUE},
      {1ull, NullInt64(), NULL_VALUE},

      // numeric
      {Numeric(0), NumericValue::MaxValue(), LESS},
      {NumericValue::MinValue(), NumericValue::MaxValue(), LESS},
      {Numeric(1), Numeric(1), EQUAL},
      {NumericFromDouble(123.456), NumericFromDouble(123.654), LESS},
      {NumericFromDouble(-123.654), NumericFromDouble(-123.456), LESS},
      {Numeric(-1), Numeric(-1), EQUAL},
      {Numeric(1), NullNumeric(), NULL_VALUE},

      // numeric vs. int64
      {Int64(0), NumericValue::MaxValue(), LESS},
      {NumericValue::MinValue(), int64min, LESS},
      {Int64(1), Numeric(1), EQUAL},
      {Int64(123), NumericFromDouble(123.654), LESS},
      {NumericFromDouble(-123.456), Int64(-123), LESS},
      {Int64(-1), Numeric(-1), EQUAL},
      {Int64(1), NullNumeric(), NULL_VALUE},

      // numeric vs. uint64
      {Uint64(0), NumericValue::MaxValue(), LESS},
      {NumericValue::MinValue(), uint64max, LESS},
      {Uint64(1), Numeric(1), EQUAL},
      {Uint64(123), NumericFromDouble(123.654), LESS},
      {NumericFromDouble(-123.456), Uint64(123), LESS},
      {Numeric(-1), Uint64(1), LESS},
      {Uint64(1), NullNumeric(), NULL_VALUE},

      // numeric vs. double
      {Double(0), NumericValue::MaxValue(), LESS},
      {doublemin, NumericValue::MinValue(), LESS},
      {double_neg_inf, NumericValue::MinValue(), LESS},
      {NumericValue::MaxValue(), double_pos_inf, LESS},
      {double_nan, NumericValue::MaxValue(), UNORDERED_BUT_ARRAY_ORDERS_LESS},
      {Double(1.0), Numeric(1), EQUAL},
      {Double(0.0), Numeric(0), EQUAL},
      {Double(-0.0), Numeric(0), EQUAL},
      {Double(123), NumericFromDouble(123.654), LESS},
      {NumericFromDouble(-123.456), Double(-123), LESS},
      {Double(-123.456), NumericFromDouble(-123.456), EQUAL},
      {Double(-1), Numeric(-1), EQUAL},
      {Double(1), NullNumeric(), NULL_VALUE},

      // bignumeric
      {BigNumeric(0), BigNumeric(BigNumericValue::MaxValue()), LESS},
      {BigNumeric(BigNumericValue::MinValue()), BigNumeric(0), LESS},
      {BigNumeric(1), BigNumeric(1), EQUAL},
      {BigNumeric(BigNumericValue::FromStringStrict("123.456").value()),
       BigNumeric(BigNumericValue::FromStringStrict("123.654").value()), LESS},
      {BigNumeric(BigNumericValue::FromStringStrict("-123.654").value()),
       BigNumeric(BigNumericValue::FromStringStrict("-123.456").value()), LESS},
      {BigNumeric(-1), BigNumeric(-1), EQUAL},
      {BigNumeric(1), NullBigNumeric(), NULL_VALUE},

      // bignumeric vs. int64
      {Int64(0), BigNumeric(BigNumericValue::MaxValue()), LESS},
      {BigNumeric(BigNumericValue::MinValue()), int64min, LESS},
      {Int64(1), BigNumeric(1), EQUAL},
      {Int64(123),
       BigNumeric(BigNumericValue::FromStringStrict("123.654").value()), LESS},
      {BigNumeric(BigNumericValue::FromStringStrict("-123.456").value()),
       Int64(-123), LESS},
      {Int64(-1), BigNumeric(-1), EQUAL},
      {Int64(1), NullBigNumeric(), NULL_VALUE},

      // bignumeric vs. uint64
      {Uint64(0), BigNumeric(BigNumericValue::MaxValue()), LESS},
      {BigNumeric(BigNumericValue::MinValue()), uint64max, LESS},
      {Uint64(1), BigNumeric(1), EQUAL},
      {Uint64(123),
       BigNumeric(BigNumericValue::FromStringStrict("123.654").value()), LESS},
      {BigNumeric(BigNumericValue::FromStringStrict("-123.456").value()),
       Uint64(123), LESS},
      {BigNumeric(-1), Uint64(1), LESS},
      {Uint64(1), NullBigNumeric(), NULL_VALUE},

      // bignumeric vs. floating point
      {Double(0), BigNumeric(BigNumericValue::MaxValue()), LESS},
      {doublemin, BigNumeric(BigNumericValue::MinValue()), LESS},
      {double_neg_inf, BigNumeric(BigNumericValue::MinValue()), LESS},
      {BigNumeric(BigNumericValue::MaxValue()), double_pos_inf, LESS},
      {BigNumeric(BigNumericValue::MaxValue()), float_pos_inf, LESS},
      {double_nan, BigNumeric(BigNumericValue::MaxValue()),
       UNORDERED_BUT_ARRAY_ORDERS_LESS},
      {Double(1.0), BigNumeric(1), EQUAL},
      {Double(0.0), BigNumeric(0), EQUAL},
      {Double(-0.0), BigNumeric(0), EQUAL},
      {Double(123),
       BigNumeric(BigNumericValue::FromStringStrict("123.654").value()), LESS},
      {BigNumeric(BigNumericValue::FromStringStrict("-123.456").value()),
       Double(-123), LESS},
      {Double(-123.456),
       BigNumeric(BigNumericValue::FromStringStrict("-123.456").value()),
       EQUAL},
      {Double(-1), BigNumeric(-1), EQUAL},
      {Double(1), NullBigNumeric(), NULL_VALUE},

      // bignumeric vs numeric
      {Numeric(0), BigNumeric(BigNumericValue::MaxValue()), LESS},
      {BigNumeric(BigNumericValue::MinValue()), Numeric(0), LESS},
      {Numeric(1), BigNumeric(1), EQUAL},
      {Numeric(NumericValue::FromStringStrict("123.456").value()),
       BigNumeric(BigNumericValue::FromStringStrict("123.654").value()), LESS},
      {Numeric(NumericValue::FromStringStrict("-123.654").value()),
       BigNumeric(BigNumericValue::FromStringStrict("-123.456").value()), LESS},
      {Numeric(-1), BigNumeric(-1), EQUAL},
      {Numeric(1), NullBigNumeric(), NULL_VALUE},
      {BigNumeric(1), NullNumeric(), NULL_VALUE},

      // string
      {"hello", "hello", EQUAL},
      {"bye", "hello", LESS},
      {"@", "A", LESS},
      {"a", "aa", LESS},
      {"Case sensitive", "case sensitive", LESS},
      {"hello world", NullString(), NULL_VALUE},
      // Cyrillic UTF-8 characters
      {"ъ", "ь", LESS},
      {"", NullString(), NULL_VALUE},

      // bytes
      {Bytes("hello"), Bytes("hello"), EQUAL},
      {Bytes("bye"), Bytes("hello"), LESS},
      {Bytes("@"), Bytes("A"), LESS},
      {Bytes("a"), Bytes("aa"), LESS},
      {Bytes("Case sensitive"), Bytes("case sensitive"), LESS},
      {Bytes("hello world"), NullBytes(), NULL_VALUE},
      // Cyrillic UTF-8 characters
      {Bytes("ъ"), Bytes("ь"), LESS},
      {Bytes(""), NullBytes(), NULL_VALUE},

      // date
      {Date(1), Date(1), EQUAL},
      {Date(-1), Date(0), LESS},
      {Date(-10), Date(-1), LESS},
      {Date(1), Date(10), LESS},
      {Date(1), NullDate(), NULL_VALUE},

      // timestamp
      {Timestamp(1), Timestamp(1), EQUAL},
      {Timestamp(-1), Timestamp(0), LESS},
      {Timestamp(-10), Timestamp(-1), LESS},
      {Timestamp(1), Timestamp(10), LESS},
      {Timestamp(1), NullTimestamp(), NULL_VALUE},

      // enum
      {enum0, enum0, EQUAL},
      {enum1, enum1, EQUAL},
      {enum0, enum1, LESS},
      {enum0, Null(TestEnumType()), NULL_VALUE},

      // time
      // micros
      {TimeMicros(1, 2, 3, 4), TimeMicros(1, 2, 3, 4), EQUAL},
      {TimeMicros(1, 2, 3, 4), TimeMicros(1, 2, 3, 5), LESS},
      {TimeMicros(1, 2, 3, 4), TimeMicros(1, 2, 5, 4), LESS},
      {TimeMicros(1, 2, 3, 4), TimeMicros(1, 5, 3, 4), LESS},
      {TimeMicros(1, 2, 3, 4), TimeMicros(5, 2, 3, 4), LESS},
      {TimeMicros(1, 2, 3, 4), NullTime(), NULL_VALUE},

      // datetime
      // micros
      {DatetimeMicros(2006, 1, 2, 3, 4, 5, 654321),
       DatetimeMicros(2006, 1, 2, 3, 4, 5, 654321), EQUAL},
      {DatetimeMicros(2006, 1, 2, 3, 4, 5, 654321),
       DatetimeMicros(2006, 1, 2, 3, 4, 5, 765432), LESS},
      {DatetimeMicros(2006, 1, 2, 3, 4, 5, 654321),
       DatetimeMicros(2006, 1, 2, 3, 4, 7, 654321), LESS},
      {DatetimeMicros(2006, 1, 2, 3, 4, 5, 654321),
       DatetimeMicros(2006, 1, 2, 3, 7, 5, 654321), LESS},
      {DatetimeMicros(2006, 1, 2, 3, 4, 5, 654321),
       DatetimeMicros(2006, 1, 2, 7, 4, 5, 654321), LESS},
      {DatetimeMicros(2006, 1, 2, 3, 4, 5, 654321),
       DatetimeMicros(2006, 1, 7, 3, 4, 5, 654321), LESS},
      {DatetimeMicros(2006, 1, 2, 3, 4, 5, 654321),
       DatetimeMicros(2006, 7, 2, 3, 4, 5, 654321), LESS},
      {DatetimeMicros(2006, 1, 2, 3, 4, 5, 654321),
       DatetimeMicros(2007, 1, 2, 3, 4, 5, 654321), LESS},
      {DatetimeMicros(2006, 1, 2, 3, 4, 5, 654321), NullDatetime(), NULL_VALUE},
  };
  std::set<LanguageFeature> features = {FEATURE_TIMESTAMP_NANOS};
  std::vector<ComparisonTest> nano_tests = {
      // time nanos
      {TimeNanos(1, 2, 3, 1234567), TimeNanos(1, 2, 3, 1234567), EQUAL,
       features},
      {TimeNanos(1, 2, 3, 1234567), TimeNanos(1, 2, 3, 1234568), LESS,
       features},
      {TimeNanos(1, 2, 3, 1234567), TimeNanos(1, 2, 5, 1234567), LESS,
       features},
      {TimeNanos(1, 2, 3, 1234567), TimeNanos(1, 5, 3, 1234567), LESS,
       features},
      {TimeNanos(1, 2, 3, 1234567), TimeNanos(5, 2, 3, 1234567), LESS,
       features},
      {TimeNanos(1, 2, 3, 1234567), NullTime(), NULL_VALUE, features},

      // datetime nanos
      {DatetimeNanos(2006, 1, 2, 3, 4, 5, 987654321),
       DatetimeNanos(2006, 1, 2, 3, 4, 5, 987654321), EQUAL, features},
      {DatetimeNanos(2006, 1, 2, 3, 4, 5, 987654321),
       DatetimeNanos(2006, 1, 2, 3, 4, 5, 987654322), LESS, features},
      {DatetimeNanos(2006, 1, 2, 3, 4, 5, 987654321),
       DatetimeNanos(2006, 1, 2, 3, 4, 7, 987654321), LESS, features},
      {DatetimeNanos(2006, 1, 2, 3, 4, 5, 987654321),
       DatetimeNanos(2006, 1, 2, 3, 7, 5, 987654321), LESS, features},
      {DatetimeNanos(2006, 1, 2, 3, 4, 5, 987654321),
       DatetimeNanos(2006, 1, 2, 7, 4, 5, 987654321), LESS, features},
      {DatetimeNanos(2006, 1, 2, 3, 4, 5, 987654321),
       DatetimeNanos(2006, 1, 7, 3, 4, 5, 987654321), LESS, features},
      {DatetimeNanos(2006, 1, 2, 3, 4, 5, 987654321),
       DatetimeNanos(2006, 7, 2, 3, 4, 5, 987654321), LESS, features},
      {DatetimeNanos(2006, 1, 2, 3, 4, 5, 987654321),
       DatetimeNanos(2007, 1, 2, 3, 4, 5, 987654321), LESS, features},
      {DatetimeNanos(2006, 1, 2, 3, 4, 5, 987654321), NullDatetime(),
       NULL_VALUE, features},
  };
  v.insert(v.end(), nano_tests.begin(), nano_tests.end());
  std::set<LanguageFeature> uuid_features = {FEATURE_UUID_TYPE};
  std::vector<ComparisonTest> uuid_tests = {
      {Uuid(UuidValue::FromString("00000000-0000-4000-8000-000000000001")
                .value()),
       Uuid(UuidValue::FromString("00000000-0000-4000-8000-000000000001")
                .value()),
       EQUAL, uuid_features},
      {Uuid(UuidValue::FromString("00000000-0000-4000-8000-000000000001")
                .value()),
       Uuid(UuidValue::FromString("00000000-0022-4000-8000-000000000022")
                .value()),
       LESS, uuid_features},
      {Uuid(UuidValue::FromString("00000000-0000-4000-8000-000000000001")
                .value()),
       NullUuid(), NULL_VALUE, uuid_features},
  };
  v.insert(v.end(), uuid_tests.begin(), uuid_tests.end());
  if (include_struct_comparisons) {
    std::vector<ComparisonTest> struct_equality_tests =
        GetStructComparisonTests();
    v.insert(v.end(), struct_equality_tests.begin(),
             struct_equality_tests.end());
  }
  if (include_array_comparisons) {
    // Add a few more tests for array-specific comparison behaviors.
    std::vector<ComparisonTest> array_comparison_tests =
        GetArrayComparisonTests();

    // Duplicate tests by nesting the values into arrays, and do the same
    // comparison on the arrays.
    for (const ComparisonTest& test : v) {
      const ArrayType* left_array_type;
      ZETASQL_CHECK_OK(type_factory()->MakeArrayType(test.left.type(),
                                             &left_array_type));
      const Value left_array = values::Array(left_array_type, {test.left});

      const ArrayType* right_array_type;
      ZETASQL_CHECK_OK(type_factory()->MakeArrayType(test.right.type(),
                                             &right_array_type));
      const Value right_array = values::Array(right_array_type, {test.right});

      // We can only compare arrays if they are the same type.  We do not
      // do implicit coercion of arrays.  So if the types of the arguments
      // are not the same then skip this test.
      if (!left_array_type->Equals(right_array_type)) {
        continue;
      }

      array_comparison_tests.push_back(ComparisonTest(
          left_array, right_array, test.result, test.required_features));
    }
    v.insert(v.end(), array_comparison_tests.begin(),
             array_comparison_tests.end());
  }
  return v;
}

// Builds a QueryParamsWithResult from <input> and <out>, and appends it
// to <result>.  Wraps <out> with a feature set-result map if needed, in
// the following cases:
//
// 1) If there is any TIME/DATETIME typed value in the input, then
//    FEATURE_CIVIL_TIME is added to the feature set.
// 2) array_language_features: flags to be added if any inputs are ARRAYs
// 3) If the <input> includes the NUMERIC type, then FEATURE_NUMERIC_TYPE
//    is added.
//
// If none of these are true, then constructs the QueryParamsWithResult
// directly from  <input>/<out>.
static void AddTestWithPossiblyWrappedResultWithRequiredFeatures(
    absl::Span<const ValueConstructor> input, const Value& out,
    const std::set<LanguageFeature>& required_language_features,
    absl::Span<const LanguageFeature> array_language_features,
    std::vector<QueryParamsWithResult>* result) {
  bool has_any_civil_time = false;
  bool has_any_numeric = false;
  bool has_any_bignumeric = false;
  bool has_any_array = false;
  // Check the inputs for civil time, numeric, and arrays.
  for (const auto& each : input) {
    if (each.type()->UsingFeatureV12CivilTimeType()) {
      has_any_civil_time = true;
    }
    if (each.type()->IsNumericType()) {
      has_any_numeric = true;
    }
    if (each.type()->IsBigNumericType()) {
      has_any_bignumeric = true;
    }
    if (each.type()->IsArray()) {
      has_any_array = true;
      if (each.type()->AsArray()->element_type()->IsNumericType()) {
        has_any_numeric = true;
      }
      if (each.type()->AsArray()->element_type()->IsBigNumericType()) {
        has_any_bignumeric = true;
      }
    }
  }
  // Check the output as well.
  if (out.type()->UsingFeatureV12CivilTimeType()) {
    has_any_civil_time = true;
  }
  if (out.type()->IsNumericType()) {
    has_any_numeric = true;
  }
  if (out.type()->IsBigNumericType()) {
    has_any_bignumeric = true;
  }
  if (out.type()->IsArray()) {
    has_any_array = true;
    if (out.type()->AsArray()->element_type()->IsNumericType()) {
      has_any_numeric = true;
    }
    if (out.type()->AsArray()->element_type()->IsBigNumericType()) {
      has_any_bignumeric = true;
    }
  }

  // Build the feature set.
  QueryParamsWithResult::FeatureSet feature_set = required_language_features;
  if (has_any_civil_time) {
    feature_set.insert(FEATURE_CIVIL_TIME);
  }
  if (has_any_numeric) {
    feature_set.insert(FEATURE_NUMERIC_TYPE);
  }
  if (has_any_array) {
    for (const LanguageFeature flag : array_language_features) {
      feature_set.insert(flag);
    }
  }
  if (has_any_bignumeric) {
    feature_set.insert(FEATURE_BIGNUMERIC_TYPE);
  }

  result->push_back(
      QueryParamsWithResult(input, out).AddRequiredFeatures(feature_set));
}

// Wraps any test cases that use NUMERIC with FEATURE_NUMERIC_TYPE.
static void WrapNumericTestCases(std::vector<QueryParamsWithResult>* tests) {
  for (auto& test_case : *tests) {
    if (test_case.required_features().empty() &&
        test_case.result().type()->IsNumericType()) {
      test_case = test_case.WrapWithFeature(FEATURE_NUMERIC_TYPE);
    }
  }
}

// Wraps any test cases that use BIGNUMERIC with FEATURE_BIGNUMERIC_TYPE.
static void WrapBigNumericTestCases(std::vector<QueryParamsWithResult>* tests) {
  for (auto& test_case : *tests) {
    if (test_case.required_features().empty() &&
        test_case.result().type()->IsBigNumericType()) {
      test_case = test_case.WrapWithFeature(FEATURE_BIGNUMERIC_TYPE);
    }
  }
}

// Wraps any test cases that use RANGE with FEATURE_RANGE_TYPE.
static void WrapRangeTestCases(std::vector<QueryParamsWithResult>* tests) {
  for (auto& test_case : *tests) {
    if (test_case.required_features().empty() &&
        test_case.result().type()->IsRangeType()) {
      test_case = test_case.AddRequiredFeature(FEATURE_RANGE_TYPE);
      // For CIVIL TIME element type we need to enable FEATURE_CIVIL_TIME.
      if (test_case.result()
              .type()
              ->AsRange()
              ->element_type()
              ->UsingFeatureV12CivilTimeType()) {
        test_case = test_case.AddRequiredFeature(FEATURE_CIVIL_TIME);
      }
    }
  }
}

std::vector<QueryParamsWithResult> GetFunctionTestsEqual() {
  std::vector<QueryParamsWithResult> result;
  for (const ComparisonTest& test : GetComparisonTests(
           /*include_struct_comparisons=*/true,
           /*include_array_comparisons=*/true)) {
    Value out;
    switch (test.result) {
      case NULL_VALUE:
        out = NullBool();
        break;
      case EQUAL:
        out = True();
        break;
      case LESS:
      case ARRAY_UNEQUAL_ORDERS_LESS:
      case UNORDERED_BUT_ARRAY_ORDERS_LESS:
        out = False();
        break;
    }
    AddTestWithPossiblyWrappedResultWithRequiredFeatures(
        {test.left, test.right}, out, test.required_features,
        /*array_language_features=*/{FEATURE_ARRAY_EQUALITY}, &result);
    AddTestWithPossiblyWrappedResultWithRequiredFeatures(
        {test.right, test.left}, out, test.required_features,
        /*array_language_features=*/{FEATURE_ARRAY_EQUALITY}, &result);
  }
  return result;
}

std::vector<QueryParamsWithResult> GetFunctionTestsNotEqual() {
  std::vector<QueryParamsWithResult> result;
  for (const ComparisonTest& test : GetComparisonTests(
           /*include_struct_comparisons=*/true,
           /*include_array_comparisons=*/true)) {
    Value out;
    switch (test.result) {
      case NULL_VALUE:
        out = NullBool();
        break;
      case LESS:
      case ARRAY_UNEQUAL_ORDERS_LESS:
      case UNORDERED_BUT_ARRAY_ORDERS_LESS:
        out = True();
        break;
      case EQUAL:
        out = False();
        break;
    }
    AddTestWithPossiblyWrappedResultWithRequiredFeatures(
        {test.left, test.right}, out, test.required_features,
        /*array_language_features=*/{FEATURE_ARRAY_EQUALITY}, &result);
    AddTestWithPossiblyWrappedResultWithRequiredFeatures(
        {test.right, test.left}, out, test.required_features,
        /*array_language_features=*/{FEATURE_ARRAY_EQUALITY}, &result);
  }
  return result;
}

std::vector<QueryParamsWithResult> GetFunctionTestsGreater() {
  std::vector<QueryParamsWithResult> result;
  for (const ComparisonTest& test : GetComparisonTests(
           /*include_struct_comparisons=*/false,
           /*include_array_comparisons=*/true)) {
    Value out;
    switch (test.result) {
      case NULL_VALUE:
        out = NullBool();
        break;
      case LESS:
      case EQUAL:
      case UNORDERED_BUT_ARRAY_ORDERS_LESS:
        out = False();
        break;
      case ARRAY_UNEQUAL_ORDERS_LESS:
        out = NullBool();
        break;
    }
    AddTestWithPossiblyWrappedResultWithRequiredFeatures(
        {test.left, test.right}, out, test.required_features,
        /*array_language_features=*/{FEATURE_ARRAY_ORDERING}, &result);
    AddTestWithPossiblyWrappedResultWithRequiredFeatures(
        {test.right, test.left}, (test.result == LESS) ? True() : out,
        test.required_features,
        /*array_language_features=*/{FEATURE_ARRAY_ORDERING}, &result);
  }
  return result;
}

std::vector<QueryParamsWithResult> GetFunctionTestsGreaterOrEqual() {
  std::vector<QueryParamsWithResult> result;
  for (const ComparisonTest& test : GetComparisonTests(
           /*include_struct_comparisons=*/false,
           /*include_array_comparisons=*/true)) {
    Value out;
    switch (test.result) {
      case NULL_VALUE:
        out = NullBool();
        break;
      case EQUAL:
        out = True();
        break;
      case LESS:
      case UNORDERED_BUT_ARRAY_ORDERS_LESS:
        out = False();
        break;
      case ARRAY_UNEQUAL_ORDERS_LESS:
        out = NullBool();
        break;
    }
    AddTestWithPossiblyWrappedResultWithRequiredFeatures(
        {test.left, test.right}, out, test.required_features,
        /*array_language_features=*/{FEATURE_ARRAY_ORDERING}, &result);
    AddTestWithPossiblyWrappedResultWithRequiredFeatures(
        {test.right, test.left}, (test.result == LESS) ? True() : out,
        test.required_features,
        /*array_language_features=*/{FEATURE_ARRAY_ORDERING}, &result);
  }
  return result;
}

std::vector<QueryParamsWithResult> GetFunctionTestsLess() {
  std::vector<QueryParamsWithResult> result;
  for (const ComparisonTest& test : GetComparisonTests(
           /*include_struct_comparisons=*/false,
           /*include_array_comparisons=*/true)) {
    Value out;
    switch (test.result) {
      case NULL_VALUE:
        out = NullBool();
        break;
      case LESS:
        out = True();
        break;
      case EQUAL:
      case UNORDERED_BUT_ARRAY_ORDERS_LESS:
        out = False();
        break;
      case ARRAY_UNEQUAL_ORDERS_LESS:
        out = NullBool();
        break;
    }
    AddTestWithPossiblyWrappedResultWithRequiredFeatures(
        {test.left, test.right}, out, test.required_features,
        /*array_language_features=*/{FEATURE_ARRAY_ORDERING}, &result);
    AddTestWithPossiblyWrappedResultWithRequiredFeatures(
        {test.right, test.left}, (test.result == LESS) ? False() : out,
        test.required_features,
        /*array_language_features=*/{FEATURE_ARRAY_ORDERING}, &result);
  }
  return result;
}

std::vector<QueryParamsWithResult> GetFunctionTestsLessOrEqual() {
  std::vector<QueryParamsWithResult> result;
  for (const ComparisonTest& test : GetComparisonTests(
           /*include_struct_comparisons=*/false,
           /*include_array_comparisons=*/true)) {
    Value out;
    switch (test.result) {
      case NULL_VALUE:
        out = NullBool();
        break;
      case EQUAL:
      case LESS:
        out = True();
        break;
      case UNORDERED_BUT_ARRAY_ORDERS_LESS:
        out = False();
        break;
      case ARRAY_UNEQUAL_ORDERS_LESS:
        out = NullBool();
        break;
    }
    AddTestWithPossiblyWrappedResultWithRequiredFeatures(
        {test.left, test.right}, out, test.required_features,
        /*array_language_features=*/{FEATURE_ARRAY_ORDERING}, &result);
    AddTestWithPossiblyWrappedResultWithRequiredFeatures(
        {test.right, test.left}, (test.result == LESS) ? False() : out,
        test.required_features,
        /*array_language_features=*/{FEATURE_ARRAY_ORDERING}, &result);
  }
  return result;
}

std::vector<QueryParamsWithResult> GetFunctionTestsInWithoutNulls() {
  std::vector<std::array<Value, 3>> type_sets = {
      {{String("a"), String("foo"), String("3523523zz5")}},
      {{Int32(4), Int32(-1), Int32(6)}},
      {{Int64(std::numeric_limits<int64_t>::lowest()), Int64(-1),
        Int64(std::numeric_limits<int64_t>::max())}},
      {{Float(std::numeric_limits<float>::lowest()),
        Float(std::numeric_limits<float>::max()),
        Float(std::numeric_limits<float>::min())}},
      {{Float(0.0f), Float(INFINITY), Float(-INFINITY)}},
      {{Double(0.0f), Double(INFINITY), Double(-INFINITY)}},
      {{Float(4.0f), Float(2.0), Float(8.0)}},
      {{Double(4.0f), Double(2.0), Double(8.0)}},
      {{Double(std::numeric_limits<double>::lowest()),
        Double(std::numeric_limits<double>::max()),
        Double(std::numeric_limits<double>::min())}},
  };
  std::vector<QueryParamsWithResult> ret;
  Value null_bool = NullBool();
  for (const auto& type_set : type_sets) {
    const Value& a = type_set[0];
    const Value& b = type_set[1];
    const Value& c = type_set[2];
    Value null = Value::Null(a.type());
    ret.insert(
        ret.end(),
        {
            // True.
            {{a, a}, true},
            {{a, a, b}, true},
            {{a, b, a}, true},
            {{b, a, b, c}, true},
            {{c, a, c}, true},
            {{b, b, c, a}, true},
            {{b, b, b, a, a}, true},
            {{b, b, b, b}, true},
            // False.
            {{a, b}, false},
            {{b, a, c}, false},
            {{b, a, a, c, c}, false},
            {{c, a, b, b}, false},
            // NULL first.
            {{null, a}, null_bool},
            {{null, a, b}, null_bool},
            {{null, a, b, c}, null_bool},
            {{null, b, b, a, a}, null_bool},
            {{null, b, b, b}, null_bool},
        });
  }

  auto nanf = Float(std::nanf(""));
  auto nan = Double(std::nan(""));
  ret.insert(ret.end(),
             {
                 // 0.0 == -0.0
                 {{Float(0.0f), Float(-0.0f)}, true},
                 {{Float(0.0f), Float(-0.0f), Float(3.14f)}, true},
                 {{Float(0.0f), Float(1.0f), Float(-0.0f), Float(3.14f)}, true},
                 {{Float(-0.0f), Float(0.0f)}, true},
                 {{Float(-0.0f), Float(0.0f), Float(3.14f)}, true},
                 {{Float(-0.0f), Float(1.0f), Float(0.0f), Float(3.14f)}, true},
                 {{Double(0.0), Double(-0.0)}, true},
                 {{Double(0.0), Double(-0.0), Double(3.14)}, true},
                 {{Double(0.0), Double(1), Double(-0.0), Double(3.14)}, true},
                 {{Double(-0.0), Double(0.0)}, true},
                 {{Double(-0.0), Double(0.0), Double(3.14)}, true},
                 {{Double(-0.0), Double(1), Double(0.0), Double(3.14)}, true},
                 // NaN != NaN
                 {{nanf, nanf}, false},
                 {{nanf, nanf, Float(3.14f)}, false},
                 {{nanf, Float(2.0f), nanf, Float(3.14f)}, false},
                 {{nan, nan}, false},
                 {{nan, nan, Double(3.14)}, false},
                 {{nan, Double(2), nan, Double(3.14)}, false},
                 // Bools.
                 {{true, true}, true},
                 {{false, false}, true},
                 {{true, false}, false},
                 {{false, true}, false},
                 {{true, true, false}, true},
                 {{false, true, false}, true},
                 {{null_bool, true}, null_bool},
             });
  return ret;
}

std::vector<QueryParamsWithResult> GetFunctionTestsInWithNulls() {
  std::vector<std::array<Value, 3>> type_sets = {
      {{String("a"), String("foo"), String("3523523zz5")}},
      {{Int32(4), Int32(-1), Int32(6)}},
      {{Int64(std::numeric_limits<int64_t>::lowest()), Int64(-1),
        Int64(std::numeric_limits<int64_t>::max())}},
      {{Float(std::numeric_limits<float>::lowest()),
        Float(std::numeric_limits<float>::max()),
        Float(std::numeric_limits<float>::min())}},
      {{Float(0.0f), Float(INFINITY), Float(-INFINITY)}},
      {{Double(0.0f), Double(INFINITY), Double(-INFINITY)}},
      {{Float(4.0f), Float(2.0), Float(8.0)}},
      {{Double(4.0f), Double(2.0), Double(8.0)}},
      {{Double(std::numeric_limits<double>::lowest()),
        Double(std::numeric_limits<double>::max()),
        Double(std::numeric_limits<double>::min())}},
  };
  std::vector<QueryParamsWithResult> ret;
  Value null_bool = NullBool();
  for (const auto& type_set : type_sets) {
    const Value& a = type_set[0];
    const Value& b = type_set[1];
    const Value& c = type_set[2];
    Value null = Value::Null(a.type());
    ret.insert(
        ret.end(),
        {
            // True with NULLs.
            {{a, a, null}, true},
            {{a, null, a}, true},
            {{a, a, null, null}, true},
            {{a, null, a}, true},
            {{a, null, null, a}, true},
            {{a, null, a, null}, true},
            {{a, null, null, a, null, null, a}, true},
            {{b, null, null, a, null, null, b}, true},
            {{b, null, null, a, null, null, b, null, b, null, a, null, null},
             true},
            // Not-True with NULLs.
            {{b, a, null}, null_bool},
            {{b, null, c}, null_bool},
            {{b, a, null, null}, null_bool},
            {{b, null, c}, null_bool},
            {{b, null, null, a}, null_bool},
            {{b, null, c, null}, null_bool},
            {{b, null, null, a, null, null, a}, null_bool},
            {{b, null, null, a, null, null, c}, null_bool},
            {{b, null, null, a, null, null, c, null, c, null, a, null, null},
             null_bool},
            // NULL first.
            {{null, b, null}, null_bool},
            {{null, null, c, null}, null_bool},
        });
  }

  ret.insert(ret.end(),
             {
                 {{true, true, null_bool}, true},
                 {{false, false, null_bool}, true},
                 {{true, false, null_bool}, null_bool},
                 {{false, true, null_bool}, null_bool},
                 {{true, true, false, null_bool}, true},
                 {{false, true, false, null_bool}, true},
             });
  return ret;
}

std::vector<QueryParamsWithResult> GetFunctionTestsIn() {
  std::vector<QueryParamsWithResult> ret = GetFunctionTestsInWithNulls();
  std::vector<QueryParamsWithResult> tests_without_nulls =
      GetFunctionTestsInWithoutNulls();
  ret.insert(ret.end(), tests_without_nulls.begin(), tests_without_nulls.end());
  return ret;
}

std::vector<QueryParamsWithResult> GetFunctionTestsStructIn() {
  const StructType* struct_type = SimpleStructType();  // a: string, b: int32
  const Value struct0 = Value::Struct(struct_type, {String("foo"), Int32(0)});
  const Value struct1 = Value::Struct(struct_type, {String("bar"), Int32(1)});
  const Value struct2 = Value::Struct(struct_type, {String("baz"), Int32(2)});
  const Value struct1_with_null1 =
      Value::Struct(struct_type, {NullString(), Int32(1)});
  const Value struct1_with_null2 =
      Value::Struct(struct_type, {String("bar"), NullInt32()});
  const Value struct_with_all_nulls =
      Value::Struct(struct_type, {NullString(), NullInt32()});
  const Value null_struct = Value::Null(struct_type);
  // These DCHECKs are only here to more clearly show the contents of
  // all the struct values.
  ABSL_DCHECK_EQ("(\"foo\", 0)",    struct0.GetSQLLiteral());
  ABSL_DCHECK_EQ("(\"bar\", 1)",    struct1.GetSQLLiteral());
  ABSL_DCHECK_EQ("(\"baz\", 2)",    struct2.GetSQLLiteral());
  ABSL_DCHECK_EQ("(NULL, 1)",       struct1_with_null1.GetSQLLiteral());
  ABSL_DCHECK_EQ("(\"bar\", NULL)", struct1_with_null2.GetSQLLiteral());
  ABSL_DCHECK_EQ("(NULL, NULL)",    struct_with_all_nulls.GetSQLLiteral());
  ABSL_DCHECK_EQ("NULL",            null_struct.GetSQLLiteral());

  return {
    {{struct0, struct0}, True()},
    {{struct0, struct1}, False()},
    {{struct0, struct0, struct1, struct2}, True()},
    {{struct1, struct0, struct1, struct2}, True()},
    {{struct2, struct0, struct1, struct2}, True()},

    {{struct0, struct1, struct2}, False()},
    {{struct0, struct1_with_null1, struct0}, True()},
    {{struct0, struct1_with_null2, struct0}, True()},
    {{struct0, struct_with_all_nulls, struct0}, True()},
    {{struct0, null_struct, struct0}, True()},
    {{struct0, null_struct, struct0}, True()},
    {{struct0, struct1_with_null1}, False()},
    {{struct0, struct1_with_null2}, False()},
    {{struct0, struct_with_all_nulls}, NullBool()},
    {{struct0, null_struct}, NullBool()},

    {{struct1, struct1_with_null1, struct0}, NullBool()},
    {{struct1, struct1_with_null2, struct0}, NullBool()},
    {{struct0, struct_with_all_nulls}, NullBool()},
    {{struct1, struct_with_all_nulls}, NullBool()},
    {{struct2, struct_with_all_nulls}, NullBool()},

    {{struct1_with_null1, struct1_with_null1}, NullBool()},
    {{struct1_with_null1, struct0, struct1}, NullBool()},
    {{struct1_with_null2, struct1_with_null2}, NullBool()},
    {{struct1_with_null2, struct0, struct1}, NullBool()},
    {{struct1_with_null2, struct0}, False()},
    {{struct1_with_null2, struct0}, False()},

    {{struct_with_all_nulls, struct0}, NullBool()},
    {{struct_with_all_nulls, struct1}, NullBool()},
    {{struct_with_all_nulls, struct2}, NullBool()},
    {{struct_with_all_nulls, struct1_with_null1}, NullBool()},
    {{struct_with_all_nulls, struct1_with_null2}, NullBool()},
    {{struct_with_all_nulls, struct_with_all_nulls}, NullBool()},
    {{struct_with_all_nulls, null_struct}, NullBool()},

    {{null_struct, struct0}, NullBool()},
    {{null_struct, struct1}, NullBool()},
    {{null_struct, struct2}, NullBool()},
    {{null_struct, struct1_with_null1}, NullBool()},
    {{null_struct, struct1_with_null2}, NullBool()},
    {{null_struct, struct_with_all_nulls}, NullBool()},
    {{null_struct, null_struct}, NullBool()},
  };
}

std::vector<QueryParamsWithResult> GetFunctionTestsIsNull() {
  const ArrayType* struct_array_type = MakeArrayType(SimpleStructType());
  const ArrayType* nullable_int_array_type =
      MakeArrayType(NullableIntProtoType());
  std::vector<QueryParamsWithResult> v = {
      // Simple types.
      {{NullInt32()},                                           True()},
      {{NullInt64()},                                           True()},
      {{NullUint32()},                                          True()},
      {{NullUint64()},                                          True()},
      {{NullBool()},                                            True()},
      {{NullFloat()},                                           True()},
      {{NullDouble()},                                          True()},
      {{NullString()},                                          True()},
      {{NullBytes()},                                           True()},

      {{Int32(1)},                                              False()},
      {{Int64(1)},                                              False()},
      {{Uint32(1)},                                             False()},
      {{Uint64(1)},                                             False()},
      {{True()},                                                False()},
      {{False()},                                               False()},
      {{Float(1.1)},                                            False()},
      {{Double(1.1)},                                           False()},
      {{String("a")},                                           False()},
      {{Bytes("b")},                                            False()},

      // Date and timestamp types.
      {{NullDate()},                                            True()},
      {{NullTimestamp()},                                       True()},

      {{Date(0)},                                               False()},
      {{Timestamp(0)},                                          False()},

      // Enum types.
      {{Value::Null(TestEnumType())},                           True()},
      {{Value::Enum(TestEnumType(), 0)},                        False()},

      // Proto types.
      {{Value::Null(KitchenSinkProtoType())},                   True()},
      {{Value::Null(NullableIntProtoType())},                   True()},

      {{KitchenSink("int64_key_1: 1 int64_key_2: 2")},          False()},
      {{NullableInt("")},                                       False()},
      {{NullableInt("value: 1")},                               False()},

      // Struct types.
      {{Value::Null(SimpleStructType())},                       True()},
      {{Value::Null(AnotherStructType())},                      True()},

      {{Value::Struct(SimpleStructType(),
                      {String("a"), Int32(1)})},                False()},
      {{Value::Struct(SimpleStructType(),
                      {NullString(), NullInt32()})},            False()},

      // Array types.
      {{Value::Null(Int32ArrayType())},                         True()},
      {{Value::Null(Uint32ArrayType())},                        True()},
      {{Value::Null(BoolArrayType())},                          True()},
      {{Value::Null(DoubleArrayType())},                        True()},
      {{Value::Null(StringArrayType())},                        True()},
      {{Value::Null(BytesArrayType())},                         True()},
      {{Value::Null(struct_array_type)},                        True()},
      {{Value::Null(nullable_int_array_type)},                  True()},

      {{Value::Array(Int64ArrayType(), {}/* empty_array */)},   False()},
      {{Value::Array(Int64ArrayType(), {Int64(0), Int64(1)})},  False()},
      {{Value::Array(Int64ArrayType(),
                     {NullInt64(), NullInt64()})},              False()},
      {{Value::Array(StringArrayType(),
                     {String("a"), String("b")})},              False()},
      {{Value::Array(struct_array_type,
                     {Value::Struct(SimpleStructType(),
                                    {NullString(),
                                     NullInt32()})})},          False()},
      {{Value::Array(nullable_int_array_type,
                     {NullableInt("")})},                       False()},
  };
  return v;
}

const Value* findArrayWithFirstNull(const Value* arr1, const Value* arr2) {
  ABSL_CHECK_EQ(arr1->type(), arr2->type());
  ABSL_CHECK_EQ(TYPE_ARRAY, arr1->type()->kind());
  ABSL_CHECK_GT(arr1->num_elements(), 0);
  ABSL_CHECK_GT(arr2->num_elements(), 0);

  for (int i = 0; i < std::min(arr1->num_elements(), arr2->num_elements());
       i++) {
    if (arr1->element(i).is_null()) {
      return arr1;
    }
    if (arr2->element(i).is_null()) {
      return arr2;
    }
  }

  ABSL_CHECK(false) << "Expected at least 1 null value in the input arrays";
  return nullptr;
}

struct ArrayFirstLastTestCase {
  ArrayFirstLastTestCase(const ValueConstructor& input,
                         const ValueConstructor& output_first,
                         const ValueConstructor& output_last,
                         absl::StatusCode status_code = OK,
                         std::set<LanguageFeature> language_features = {})
      : input(input.get()),
        output_first(output_first.get()),
        output_last(output_last.get()),
        status_code(status_code),
        language_features(std::move(language_features)) {}

  Value input;
  Value output_first;
  Value output_last;
  absl::StatusCode status_code;
  std::set<LanguageFeature> language_features;
};

static const std::vector<ArrayFirstLastTestCase>
GetNullArrayFirstLastTestCases() {
  return {
      {Value::Null(FloatArrayType()), NullFloat(), NullFloat()},
      {Value::Null(DoubleArrayType()), NullDouble(), NullDouble()},
      {Value::Null(Int32ArrayType()), NullInt32(), NullInt32()},
      {Value::Null(Int64ArrayType()), NullInt64(), NullInt64()},
      {Value::Null(Uint32ArrayType()), NullUint32(), NullUint32()},
      {Value::Null(Uint64ArrayType()), NullUint64(), NullUint64()},
      {Value::Null(BoolArrayType()), NullBool(), NullBool()},
      {Value::Null(StringArrayType()), NullString(), NullString()},
      {Value::Null(BytesArrayType()), NullBytes(), NullBytes()},
      {Value::Null(TimestampArrayType()), NullTimestamp(), NullTimestamp()},
      {Value::Null(DateArrayType()), NullDate(), NullDate()},
      {Value::Null(NumericArrayType()),
       NullNumeric(),
       NullNumeric(),
       OK,
       {FEATURE_NUMERIC_TYPE}},
      {Value::Null(BigNumericArrayType()),
       NullBigNumeric(),
       NullBigNumeric(),
       OK,
       {FEATURE_BIGNUMERIC_TYPE}},
      {Value::Null(GeographyArrayType()),
       NullGeography(),
       NullGeography(),
       OK,
       {FEATURE_GEOGRAPHY}},
      {Value::Null(JsonArrayType()),
       NullJson(),
       NullJson(),
       OK,
       {FEATURE_JSON_TYPE}},
      {Value::Null(IntervalArrayType()),
       NullInterval(),
       NullInterval(),
       OK,
       {FEATURE_INTERVAL_TYPE}},
      {Value::Null(TimeArrayType()),
       NullTime(),
       NullTime(),
       OK,
       {FEATURE_CIVIL_TIME}},
      {Value::Null(DatetimeArrayType()),
       NullDatetime(),
       NullDatetime(),
       OK,
       {FEATURE_CIVIL_TIME}},
  };
}

static const std::vector<ArrayFirstLastTestCase>
GetEmptyArrayFirstLastTestCases() {
  return {
      {Value::EmptyArray(DoubleArrayType()), NullDouble(), NullDouble(),
       OUT_OF_RANGE},
      {Value::EmptyArray(FloatArrayType()), NullFloat(), NullFloat(),
       OUT_OF_RANGE},
      {Value::EmptyArray(StringArrayType()), NullString(), NullString(),
       OUT_OF_RANGE},
      {Value::EmptyArray(BoolArrayType()), NullBool(), NullBool(),
       OUT_OF_RANGE},
      {Value::EmptyArray(Int64ArrayType()), NullInt64(), NullInt64(),
       OUT_OF_RANGE},
      {Value::EmptyArray(Int32ArrayType()), NullInt32(), NullInt32(),
       OUT_OF_RANGE},
      {Value::EmptyArray(Uint64ArrayType()), NullUint64(), NullUint64(),
       OUT_OF_RANGE},
      {Value::EmptyArray(Uint32ArrayType()), NullUint32(), NullUint32(),
       OUT_OF_RANGE},
      {Value::EmptyArray(BytesArrayType()), NullBytes(), NullBytes(),
       OUT_OF_RANGE},
      {Value::EmptyArray(TimestampArrayType()), NullTimestamp(),
       NullTimestamp(), OUT_OF_RANGE},
      {Value::EmptyArray(DateArrayType()), NullDate(), NullDate(),
       OUT_OF_RANGE},
      {Value::EmptyArray(NumericArrayType()),
       NullNumeric(),
       NullNumeric(),
       OUT_OF_RANGE,
       {FEATURE_NUMERIC_TYPE}},
      {Value::EmptyArray(BigNumericArrayType()),
       NullBigNumeric(),
       NullBigNumeric(),
       OUT_OF_RANGE,
       {FEATURE_BIGNUMERIC_TYPE}},
      {Value::EmptyArray(GeographyArrayType()),
       NullGeography(),
       NullGeography(),
       OUT_OF_RANGE,
       {FEATURE_GEOGRAPHY}},
      {Value::EmptyArray(JsonArrayType()),
       NullJson(),
       NullJson(),
       OUT_OF_RANGE,
       {FEATURE_JSON_TYPE}},
      {Value::EmptyArray(IntervalArrayType()),
       NullInterval(),
       NullInterval(),
       OUT_OF_RANGE,
       {FEATURE_INTERVAL_TYPE}},
      {Value::EmptyArray(TimeArrayType()),
       NullTime(),
       NullTime(),
       OUT_OF_RANGE,
       {FEATURE_CIVIL_TIME}},
      {Value::EmptyArray(DatetimeArrayType()),
       NullDatetime(),
       NullDatetime(),
       OUT_OF_RANGE,
       {FEATURE_CIVIL_TIME}},
  };
}

static const std::vector<ArrayFirstLastTestCase> GetArrayFirstLastTestCases() {
  // a: string, b: int32
  const StructType* struct_type = SimpleStructType();
  const ArrayType* struct_array_type;
  ZETASQL_CHECK_OK(type_factory()->MakeArrayType(struct_type, &struct_array_type));
  const Value null_struct = Value::Null(struct_type);
  const Value struct_without_null =
      Value::Struct(struct_type, {String("foo"), Int32(0)});
  const Value struct_with_null =
      Value::Struct(struct_type, {NullString(), NullInt32()});

  // a: string, b: {a: string b: int32}
  const StructType* nested_struct_type;
  ZETASQL_CHECK_OK(type_factory()->MakeStructType(
      {{"a", StringType()}, {"b", struct_type}}, &nested_struct_type));
  const ArrayType* nested_struct_array_type;
  ZETASQL_CHECK_OK(type_factory()->MakeArrayType(nested_struct_type,
                                         &nested_struct_array_type));
  const Value null_nested_struct = Value::Null(nested_struct_type);
  const Value nested_struct_without_null =
      Value::Struct(nested_struct_type, {String("x"), struct_without_null});
  const Value nested_struct_with_null =
      Value::Struct(nested_struct_type, {NullString(), struct_without_null});

  std::vector<ArrayFirstLastTestCase> test_cases = {
      // Null array
      {Value::Null(struct_array_type), null_struct, null_struct},
      {Value::Null(nested_struct_array_type), null_nested_struct,
       null_nested_struct},

      // Empty array
      {Value::EmptyArray(struct_array_type), null_struct, null_struct,
       OUT_OF_RANGE},
      {Value::EmptyArray(nested_struct_array_type), null_nested_struct,
       null_nested_struct, OUT_OF_RANGE},

      // Double array
      {values::Array(DoubleArrayType(), {NullDouble(), Value::Double(1)}),
       NullDouble(), Value::Double(1)},

      // Int array
      {values::Int64Array({5, 3, 2}), Value::Int64(5), Value::Int64(2)},

      // String array
      {values::StringArray({"hello", "WORLD", "world"}), Value::String("hello"),
       Value::String("world")},

      // Struct array
      {values::Array(struct_array_type,
                     {struct_without_null, struct_with_null, null_struct}),
       struct_without_null, null_struct},

      // Nested struct array
      {values::Array(nested_struct_array_type,
                     {null_nested_struct, nested_struct_with_null,
                      nested_struct_without_null}),
       null_nested_struct, nested_struct_without_null},
  };
  std::vector<ArrayFirstLastTestCase> null_test_cases =
      GetNullArrayFirstLastTestCases();
  std::vector<ArrayFirstLastTestCase> empty_test_cases =
      GetEmptyArrayFirstLastTestCases();
  absl::c_move(null_test_cases, std::back_inserter(test_cases));
  absl::c_move(empty_test_cases, std::back_inserter(test_cases));
  return test_cases;
}

static void AddWrappedSafeArrayFunctionResult(
    absl::Span<const ValueConstructor> input, const Value& out,
    absl::StatusCode status_code,
    const std::set<LanguageFeature>& array_language_features, bool is_safe,
    std::vector<QueryParamsWithResult>* result) {
  // Build the feature set.
  QueryParamsWithResult::FeatureSet feature_set;
  for (const LanguageFeature& feature : array_language_features) {
    feature_set.insert(feature);
  }

  if (is_safe) {
    feature_set.insert(FEATURE_SAFE_FUNCTION_CALL);
    result->push_back(
        QueryParamsWithResult(input, out).WrapWithFeatureSet(feature_set));
  } else {
    result->push_back(QueryParamsWithResult(input, out, status_code)
                          .WrapWithFeatureSet(feature_set));
  }
}

struct TypeFeaturePair {
  TypeFeaturePair(const Type* type, const Value& example1,
                  const Value& example2, const Value& example3,
                  const Value& example4)
      : type(type),
        example_input_1(example1),
        example_input_2(example2),
        example_input_3(example3),
        example_input_4(example4),
        required_features({}) {}

  TypeFeaturePair(const Type* type, const Value& example1,
                  const Value& example2, const Value& example3)
      : TypeFeaturePair(type, example1, example2, example3, Value()) {}

  TypeFeaturePair(const Type* type, const Value& example1,
                  const Value& example2, const Value& example3,
                  const Value& example4, LanguageFeature feature)
      : type(type),
        example_input_1(example1),
        example_input_2(example2),
        example_input_3(example3),
        example_input_4(example4),
        required_features({feature}) {}

  TypeFeaturePair(const Type* type, const Value& example1,
                  const Value& example2, const Value& example3,
                  const Value& example4,
                  std::initializer_list<LanguageFeature> features)
      : type(type),
        example_input_1(example1),
        example_input_2(example2),
        example_input_3(example3),
        example_input_4(example4),
        required_features(features.begin(), features.end()) {}

  TypeFeaturePair(const Type* type, const Value& example1,
                  const Value& example2, const Value& example3,
                  LanguageFeature feature)
      : TypeFeaturePair(type, example1, example2, example3, Value(), feature) {}

  const Type* type;
  Value example_input_1;
  Value example_input_2;
  Value example_input_3;
  Value example_input_4;
  std::set<LanguageFeature> required_features;
};

static const std::vector<TypeFeaturePair> GetArrayTypesWithFeatures() {
  const std::string kitchen_sink_string_1("int64_key_1: 1 int64_key_2: 2");
  const std::string kitchen_sink_string_2(
      "int64_key_1: 1 int64_key_2: 2 repeated_int32_val: 3 "
      "repeated_int32_val: 4");
  const std::string kitchen_sink_string_3(
      "int64_key_1: 1 int64_key_2: 2 bool_val: true float_val: -1.5");
  const std::string nullable_int_string_1("value: 1");
  const std::string nullable_int_string_2("value: 2000000");

  return {
      {FloatType(), Float(1.2), Float(-3.3), Float(float_nan)},
      {DoubleType(), Double(double_pos_inf), Double(1.2), Double(double_nan)},
      {Int32Type(), Int32(1), Int32(-3), Int32(0x7FFFFFFF)},
      {Int64Type(), Int64(0x7FFFFFFFFFFFFFFF), Int64(1), Int64(-3)},
      {Uint32Type(), Uint32(1), Uint32(0x7FFFFFFF), Uint32(0xFFFFFFFFU)},
      {Uint64Type(), Uint64(0x7FFFFFFFFFFFFFFFUL), Uint64(0xFFFFFFFFFFFFFFFFU),
       Uint64(-3)},
      {BoolType(), Bool(true), Bool(false), Bool(false)},
      {StringType(), String(""), String("foo"), String("\\\\\\\\\\")},
      {BytesType(), Bytes("0x00"), Bytes("foo"), Bytes("0xFF")},
      {TimestampType(), Timestamp(types::kTimestampMax), Timestamp(-1),
       Timestamp(1500000000)},
      {DateType(), DateFromStr("1960-01-07"), Date(-1), Date(0)},
      {NumericType(), NumericFromDouble(1.2), Numeric(-3),
       Numeric(NumericValue::MaxValue()), FEATURE_NUMERIC_TYPE},
      {BigNumericType(),
       BigNumeric(BigNumericValue::FromStringStrict("123.456").value()),
       BigNumeric(BigNumericValue::MaxValue()),
       BigNumeric(BigNumericValue::MinValue()), FEATURE_BIGNUMERIC_TYPE},
      {JsonType(), Json(JSONValue(std::string("json1"))),
       Json(JSONValue(int64_t{3})), Json(JSONValue(true)), FEATURE_JSON_TYPE},
      {IntervalType(), Interval(IntervalValue::FromDays(int64_t{3}).value()),
       Interval(IntervalValue::MinValue()), Interval(IntervalValue::MaxValue()),
       FEATURE_INTERVAL_TYPE},
      {TimeType(), TimeFromStr("12:34:56.000123"), TimeMicros(1, 2, 3, 4),
       TimeMicros(1, 2, 3, 123450), FEATURE_CIVIL_TIME},
      {DatetimeType(), DatetimeMicros(2006, 1, 2, 3, 4, 5, 654321),
       DatetimeMicros(2020, 2, 10, 12, 34, 56, 789123),
       DatetimeFromStr("2017-06-25 12:34:56.123456"), FEATURE_CIVIL_TIME},
      // STRUCT a: string, b: int32
      {SimpleStructType(),
       Value::Struct(SimpleStructType(), {String(""), Int32(0)}),
       Value::Struct(SimpleStructType(), {String("foo"), Int32(1)}),
       Value::Struct(SimpleStructType(), {String("bar"), Int32(-3)})},
      // STRUCT a: string
      {AnotherStructType(), Value::Struct(AnotherStructType(), {String("foo")}),
       Value::Struct(AnotherStructType(), {String("bar")}),
       Value::Struct(AnotherStructType(), {String("")})},
      // Proto
      {KitchenSinkProtoType(), KitchenSink(kitchen_sink_string_1),
       KitchenSink(kitchen_sink_string_2), KitchenSink(kitchen_sink_string_3)},
      {NullableIntProtoType(), Proto(NullableIntProtoType(), absl::Cord("")),
       NullableInt(nullable_int_string_1), NullableInt(nullable_int_string_2)},
      // Enum
      {TestEnumType(), Value::Enum(TestEnumType(), 0x000000002),
       Value::Enum(TestEnumType(), 0), Value::Enum(TestEnumType(), 1)},
  };
}

static const std::vector<QueryParamsWithResult> GetArraySliceTestCases(
    bool is_safe) {
  std::vector<QueryParamsWithResult> test_cases;
  std::vector<TypeFeaturePair> pairs = GetArrayTypesWithFeatures();
  ABSL_DCHECK_GT(pairs.size(), 0);
  for (const TypeFeaturePair& v : pairs) {
    QueryParamsWithResult::FeatureSet feature_set;
    if (is_safe) {
      feature_set.insert(FEATURE_SAFE_FUNCTION_CALL);
    }

    if (!v.required_features.empty()) {
      for (const LanguageFeature& feature : v.required_features) {
        feature_set.insert(feature);
      }
    }

    // Setup array values with all of the possible expected slices.
    // They will be used in both input and output of the test cases.
    const ArrayType* array_type = MakeArrayType(v.type, type_factory());
    Value input123 = values::Array(
        array_type, {v.example_input_1, v.example_input_2, v.example_input_3});
    Value input_with_null = values::Array(
        array_type, {v.example_input_1, Null(v.type), v.example_input_3});
    Value slice12 =
        values::Array(array_type, {v.example_input_1, v.example_input_2});
    Value slice23 =
        values::Array(array_type, {v.example_input_2, v.example_input_3});
    Value slice1 = values::Array(array_type, {v.example_input_1});
    Value slice2 = values::Array(array_type, {v.example_input_2});
    Value slice3 = values::Array(array_type, {v.example_input_3});
    Value slice_null = values::Array(array_type, {Null(v.type)});

    // 1. Empty array argument
    test_cases.push_back(
        QueryParamsWithResult(
            {Value::EmptyArray(array_type), Value::Int64(0), Value::Int64(1)},
            Value::EmptyArray(array_type))
            .WrapWithFeatureSet(feature_set));

    // 2. NULL arguments
    // 2.1 NULL array argument
    test_cases.push_back(
        QueryParamsWithResult(
            {Value::Null(array_type), Value::Int64(0), Value::Int64(1)},
            Value::Null(array_type))
            .WrapWithFeatureSet(feature_set));

    // 2.2 NULL start argument with non-NULL array input
    test_cases.push_back(
        QueryParamsWithResult({input123, NullInt64(), Value::Int64(1)},
                              Value::Null(array_type))
            .WrapWithFeatureSet(feature_set));

    // 2.3 NULL end argument with non-NULL array input
    test_cases.push_back(
        QueryParamsWithResult({input123, Value::Int64(0), NullInt64()},
                              Value::Null(array_type))
            .WrapWithFeatureSet(feature_set));

    // 2.4 NULL start and end argument with NULL array input
    test_cases.push_back(QueryParamsWithResult({Value::Null(array_type),
                                                NullInt64(), NullInt64()},
                                               Value::Null(array_type))
                             .WrapWithFeatureSet(feature_set));

    // 3. Slice range within the bounds of array input without NULL elements
    // 3.1 Positive start and end arguments, slice range from the middle
    test_cases.push_back(
        QueryParamsWithResult({input123, Value::Int64(1), Value::Int64(1)},
                              slice2)
            .WrapWithFeatureSet(feature_set));

    // 3.2 Negative start and end arguments, slice range from the middle
    test_cases.push_back(
        QueryParamsWithResult({input123, Value::Int64(-2), Value::Int64(-2)},
                              slice2)
            .WrapWithFeatureSet(feature_set));

    // 3.3 Positive start and end arguments, slice range involving boundaries
    test_cases.push_back(
        QueryParamsWithResult({input123, Value::Int64(2), Value::Int64(2)},
                              slice3)
            .WrapWithFeatureSet(feature_set));

    // 3.4 Negative start and end arguments, slice range involving boundaries
    test_cases.push_back(
        QueryParamsWithResult({input123, Value::Int64(-1), Value::Int64(-1)},
                              slice3)
            .WrapWithFeatureSet(feature_set));

    // 3.5 Positive start and end arguments, slice range involving boundaries
    test_cases.push_back(
        QueryParamsWithResult({input123, Value::Int64(0), Value::Int64(1)},
                              slice12)
            .WrapWithFeatureSet(feature_set));

    // 3.6 Negative start and end arguments, slice range involving boundaries
    test_cases.push_back(
        QueryParamsWithResult({input123, Value::Int64(-2), Value::Int64(-1)},
                              slice23)
            .WrapWithFeatureSet(feature_set));

    // 3.7 Positive start and end arguments, slice range is entire array
    test_cases.push_back(
        QueryParamsWithResult({input123, Value::Int64(0), Value::Int64(2)},
                              input123)
            .WrapWithFeatureSet(feature_set));

    // 3.8 Negative start and end arguments, slice range is entire array
    test_cases.push_back(
        QueryParamsWithResult({input123, Value::Int64(-3), Value::Int64(-1)},
                              input123)
            .WrapWithFeatureSet(feature_set));

    // 3.9 Negative start and positive end arguments, slicing the middle range
    test_cases.push_back(
        QueryParamsWithResult({input123, Value::Int64(-2), Value::Int64(1)},
                              slice2)
            .WrapWithFeatureSet(feature_set));

    // 3.10 Negative start and positive end arguments, slicing the boundaries
    test_cases.push_back(
        QueryParamsWithResult({input123, Value::Int64(-3), Value::Int64(0)},
                              slice1)
            .WrapWithFeatureSet(feature_set));

    // 3.11 Negative start and positive end arguments, slicing the entire array
    test_cases.push_back(
        QueryParamsWithResult({input123, Value::Int64(-3), Value::Int64(2)},
                              input123)
            .WrapWithFeatureSet(feature_set));

    // 4. Slice range within the bounds of array input with NULL elements
    // 4.1 Positive start and end arguments, slice range from the middle
    test_cases.push_back(
        QueryParamsWithResult(
            {input_with_null, Value::Int64(1), Value::Int64(1)}, slice_null)
            .WrapWithFeatureSet(feature_set));

    // 4.2 Negative start and end arguments, slice range from the middle
    test_cases.push_back(
        QueryParamsWithResult(
            {input_with_null, Value::Int64(-2), Value::Int64(-2)}, slice_null)
            .WrapWithFeatureSet(feature_set));

    // 4.3 Positive start and end arguments, slice range involving boundaries
    test_cases.push_back(
        QueryParamsWithResult(
            {input_with_null, Value::Int64(2), Value::Int64(2)}, slice3)
            .WrapWithFeatureSet(feature_set));

    // 4.4 Negative start and end arguments, slice range involving boundaries
    test_cases.push_back(
        QueryParamsWithResult(
            {input_with_null, Value::Int64(-1), Value::Int64(-1)}, slice3)
            .WrapWithFeatureSet(feature_set));

    // 4.5 Positive start and end arguments, slice range involving boundaries
    test_cases.push_back(
        QueryParamsWithResult(
            {input_with_null, Value::Int64(0), Value::Int64(1)},
            values::Array(array_type, {v.example_input_1, Null(v.type)}))
            .WrapWithFeatureSet(feature_set));

    // 4.6 Negative start and end arguments, slice range involving boundaries
    test_cases.push_back(
        QueryParamsWithResult(
            {input_with_null, Value::Int64(-2), Value::Int64(-1)},
            values::Array(array_type, {Null(v.type), v.example_input_3}))
            .WrapWithFeatureSet(feature_set));

    // 4.7 Positive start and end arguments, slicing the entire array
    test_cases.push_back(
        QueryParamsWithResult(
            {input_with_null, Value::Int64(0), Value::Int64(2)},
            input_with_null)
            .WrapWithFeatureSet(feature_set));

    // 4.8 Negative start and end arguments, slicing the entire array
    test_cases.push_back(
        QueryParamsWithResult(
            {input_with_null, Value::Int64(-3), Value::Int64(-1)},
            input_with_null)
            .WrapWithFeatureSet(feature_set));

    // 5. Inverted range (start > end)
    // 5.1 Positive start and end arguments, slicing invalid range
    test_cases.push_back(
        QueryParamsWithResult({input123, Value::Int64(2), Value::Int64(1)},
                              values::EmptyArray(array_type))
            .WrapWithFeatureSet(feature_set));

    // 5.2 Negative start and end arguments, slicing invalid range
    test_cases.push_back(
        QueryParamsWithResult({input123, Value::Int64(-1), Value::Int64(-2)},
                              values::EmptyArray(array_type))
            .WrapWithFeatureSet(feature_set));

    // 5.3 Positive start and negative end arguments, slicing invalid range
    test_cases.push_back(
        QueryParamsWithResult({input123, Value::Int64(5), Value::Int64(-5)},
                              values::EmptyArray(array_type))
            .WrapWithFeatureSet(feature_set));

    // 5.4 Positive start and negative end arguments, slicing the middle range
    test_cases.push_back(
        QueryParamsWithResult({input123, Value::Int64(1), Value::Int64(-2)},
                              slice2)
            .WrapWithFeatureSet(feature_set));

    // 5.5 Positive start and negative end arguments, slicing the boundaries
    test_cases.push_back(
        QueryParamsWithResult({input123, Value::Int64(0), Value::Int64(-3)},
                              slice1)
            .WrapWithFeatureSet(feature_set));

    // 5.6 Positive start and negative end arguments, slicing the entire array
    test_cases.push_back(
        QueryParamsWithResult({input123, Value::Int64(0), Value::Int64(-1)},
                              input123)
            .WrapWithFeatureSet(feature_set));

    // 6. Out-of-bound at one end
    // 6.1 Positive start and end arguments, slicing the upper boundaries
    test_cases.push_back(
        QueryParamsWithResult({input123, Value::Int64(1), Value::Int64(5)},
                              slice23)
            .WrapWithFeatureSet(feature_set));

    // 6.2 Negative start and end arguments, slicing the lower boundaries
    test_cases.push_back(
        QueryParamsWithResult({input123, Value::Int64(-4), Value::Int64(-2)},
                              slice12)
            .WrapWithFeatureSet(feature_set));

    // 7. Out-of-bound at both ends
    // 7.1 Positive start and end arguments, slicing invalid range in a short
    // array (len = 1)
    test_cases.push_back(
        QueryParamsWithResult({values::Array(array_type, {v.example_input_1}),
                               Value::Int64(2), Value::Int64(2)},
                              values::EmptyArray(array_type))
            .WrapWithFeatureSet(feature_set));

    // 7.2 Negative start and end arguments, slicing invalid range in a short
    // array (len = 1)
    test_cases.push_back(
        QueryParamsWithResult({input123, Value::Int64(-5), Value::Int64(-4)},
                              values::EmptyArray(array_type))
            .WrapWithFeatureSet(feature_set));

    // 7.3 Positive start and end arguments, slicing invalid range in a longer
    // array (len = 3)
    test_cases.push_back(
        QueryParamsWithResult({input123, Value::Int64(3), Value::Int64(4)},
                              values::EmptyArray(array_type))
            .WrapWithFeatureSet(feature_set));

    // 7.4 Negative start and end arguments, slicing invalid range in a longer
    // array (len = 3)
    test_cases.push_back(
        QueryParamsWithResult({input123, Value::Int64(-5), Value::Int64(-4)},
                              values::EmptyArray(array_type))
            .WrapWithFeatureSet(feature_set));

    // 7.5 Negative start and positive end arguments, slicing entire array
    test_cases.push_back(
        QueryParamsWithResult({input123, Value::Int64(-5), Value::Int64(5)},
                              input123)
            .WrapWithFeatureSet(feature_set));

    // 8 Extreme values for start and end
    test_cases.push_back(
        QueryParamsWithResult(
            {input123, Value::Int64(int64max), Value::Int64(int64max)},
            values::EmptyArray(array_type))
            .AddRequiredFeatures(feature_set));
    test_cases.push_back(
        QueryParamsWithResult(
            {input123, Value::Int64(int64min), Value::Int64(int64min)},
            values::EmptyArray(array_type))
            .AddRequiredFeatures(feature_set));
    test_cases.push_back(
        QueryParamsWithResult(
            {input123, Value::Int64(int64max), Value::Int64(int64min)},
            values::EmptyArray(array_type))
            .AddRequiredFeatures(feature_set));
    test_cases.push_back(
        QueryParamsWithResult(
            {input123, Value::Int64(int64min), Value::Int64(int64max)},
            input123)
            .AddRequiredFeatures(feature_set));
  }
  return test_cases;
}

// Generates test cases from the design spec for the ARRAY_FIRST_N,
// ARRAY_LAST_N, ARRAY_REMOVE_FIRST_N, ARRAY_REMOVE_LAST_N family of functions.
// These tests are not intended to provide significant coverage, but should be
// straightforward and readable to verify the most basic functionality.
// See: (broken link)
enum ArrayPrefixSuffixFunctionName {
  kArrayFirstN,
  kArrayLastN,
  kArrayRemoveFirstN,
  kArrayRemoveLastN
};
static std::vector<QueryParamsWithResult> GetArrayFirstNLastNDesignDocTests(
    bool is_safe, ArrayPrefixSuffixFunctionName kind) {
  const absl::StatusCode kErrorCode =
      is_safe ? absl::StatusCode::kOk : absl::StatusCode::kOutOfRange;

  Value i0 = Value::Int64(0);
  Value i1 = Value::Int64(1);
  Value i2 = Value::Int64(2);
  Value i3 = Value::Int64(3);
  Value i4 = Value::Int64(4);
  Value i5 = Value::Int64(5);
  Value i6 = Value::Int64(6);
  const ArrayType* type = nullptr;
  ZETASQL_CHECK_OK(type_factory()->MakeArrayType(i0.type(), &type));
  Value arr_12345 = *Value::MakeArray(type, {i1, i2, i3, i4, i5});
  Value arr_12 = *Value::MakeArray(type, {i1, i2});
  Value arr_45 = *Value::MakeArray(type, {i4, i5});
  Value arr_123 = *Value::MakeArray(type, {i1, i2, i3});
  Value arr_345 = *Value::MakeArray(type, {i3, i4, i5});
  Value arr_empty = Value::EmptyArray(type);
  Value arr_null = Value::Null(type);

  std::vector<QueryParamsWithResult> test_cases;
  // Error cases are common to all functions.
  test_cases.push_back(QueryParamsWithResult({arr_12345, Value::Int64(-1)},
                                             arr_null, kErrorCode));
  test_cases.push_back(QueryParamsWithResult({arr_12345, Value::Int64(-3)},
                                             arr_null, kErrorCode));
  test_cases.push_back(QueryParamsWithResult({arr_12345, Value::Int64(-6)},
                                             arr_null, kErrorCode));

  // Null cases are common to all functions.
  test_cases.push_back(QueryParamsWithResult({arr_null, i0}, arr_null));
  test_cases.push_back(
      QueryParamsWithResult({arr_12345, Value::NullInt64()}, arr_null));

  // Cases that differ.
  switch (kind) {
    case kArrayFirstN:
      test_cases.push_back(QueryParamsWithResult({arr_12345, i0}, arr_empty));
      test_cases.push_back(QueryParamsWithResult({arr_12345, i2}, arr_12));
      test_cases.push_back(QueryParamsWithResult({arr_12345, i3}, arr_123));
      test_cases.push_back(QueryParamsWithResult({arr_12345, i5}, arr_12345));
      test_cases.push_back(QueryParamsWithResult({arr_12345, i6}, arr_12345));
      break;
    case kArrayLastN:
      test_cases.push_back(QueryParamsWithResult({arr_12345, i0}, arr_empty));
      test_cases.push_back(QueryParamsWithResult({arr_12345, i2}, arr_45));
      test_cases.push_back(QueryParamsWithResult({arr_12345, i3}, arr_345));
      test_cases.push_back(QueryParamsWithResult({arr_12345, i5}, arr_12345));
      test_cases.push_back(QueryParamsWithResult({arr_12345, i6}, arr_12345));
      break;
    case kArrayRemoveFirstN:
      test_cases.push_back(QueryParamsWithResult({arr_12345, i0}, arr_12345));
      test_cases.push_back(QueryParamsWithResult({arr_12345, i2}, arr_345));
      test_cases.push_back(QueryParamsWithResult({arr_12345, i3}, arr_45));
      test_cases.push_back(QueryParamsWithResult({arr_12345, i5}, arr_empty));
      test_cases.push_back(QueryParamsWithResult({arr_12345, i6}, arr_empty));
      break;
    case kArrayRemoveLastN:
      test_cases.push_back(QueryParamsWithResult({arr_12345, i0}, arr_12345));
      test_cases.push_back(QueryParamsWithResult({arr_12345, i2}, arr_123));
      test_cases.push_back(QueryParamsWithResult({arr_12345, i3}, arr_12));
      test_cases.push_back(QueryParamsWithResult({arr_12345, i5}, arr_empty));
      test_cases.push_back(QueryParamsWithResult({arr_12345, i6}, arr_empty));
      break;
  }

  if (is_safe) {
    for (auto& test_case : test_cases) {
      test_case.AddRequiredFeature(FEATURE_SAFE_FUNCTION_CALL);
    }
  }
  return test_cases;
}

// For more complete coverage of ARRAY_FIRST_N, ARRAY_LAST_N,
// ARRAY_REMOVE_FIRST_N, ARRAY_REMOVE_LAST_N, we pull from the extensive set
// of ARRAY_SLICE tests and re-apply relevant ones that slice off a prefix or
// a suffix.
struct PrefixOrSuffixTestCase {
  QueryParamsWithResult array_slice_test;
  // This is the length of the prefix or suffix to be returned, or nullopt if
  // the relevant parameter from the array slice test was SQL NULL.
  std::optional<int64_t> length = std::nullopt;

  const Value& input_arr() const { return array_slice_test.param(0); }
  const Value& result() const { return array_slice_test.result(); }
  void CopyFeaturesTo(QueryParamsWithResult& new_test) const {
    new_test.AddRequiredFeatures(array_slice_test.required_features());
    new_test.AddProhibitedFeatures(array_slice_test.prohibited_features());
  }
};
std::vector<PrefixOrSuffixTestCase> GetPrefixCasesFromArraySlice(bool is_safe) {
  std::vector<PrefixOrSuffixTestCase> test_cases;
  for (const auto& slice_test : GetArraySliceTestCases(is_safe)) {
    const Value& input_array = slice_test.param(0);
    const Value& slice_start_offset = slice_test.param(1);
    const Value& slice_end_offset = slice_test.param(2);
    if (slice_start_offset.is_null()) {
      continue;  // Skip when we cannot determine if it is a prefix.
    }
    bool is_prefix_by_start_zero = slice_start_offset.int64_value() == 0;
    bool is_prefix_by_start_neg_len =
        !input_array.is_null() &&
        slice_start_offset.int64_value() == -input_array.num_elements();
    bool is_prefix = is_prefix_by_start_zero || is_prefix_by_start_neg_len;
    if (!is_prefix) {
      continue;  // Skip test cases that are not prefixes.
    }
    if (slice_end_offset.is_null()) {
      // This is a useful test case with a null length argument.
      test_cases.push_back({.array_slice_test = slice_test});
      continue;
    }
    int64_t end_offset = slice_end_offset.int64_value();
    if (end_offset < 0) {
      // The ARRAY_SLICE function allows negative offsets to mean the offset
      // from the end of the array. For prefix cases, we need the offset from
      // the front, which means we need to adjust by the array length.
      if (input_array.is_null()) {
        continue;  // Skip test that can't be translated into offset from front.
      }
      end_offset += input_array.num_elements();
    }
    // Because ARRAY_SLICE uses an inclusive range, add one.
    int64_t length = end_offset + 1;
    test_cases.push_back({.array_slice_test = slice_test, .length = length});
  }
  return test_cases;
}
std::vector<PrefixOrSuffixTestCase> GetSuffixCasesFromArraySlice(bool is_safe) {
  std::vector<PrefixOrSuffixTestCase> test_cases;
  for (const auto& slice_test : GetArraySliceTestCases(is_safe)) {
    const Value& input_array = slice_test.param(0);
    const Value& slice_start_offset = slice_test.param(1);
    const Value& slice_end_offset = slice_test.param(2);
    if (slice_end_offset.is_null()) {
      continue;  // Skip when we cannot determine if it is a suffix.
    }
    bool is_suffix_by_end_neg_one = slice_end_offset.int64_value() == -1;
    bool is_suffix_by_end_len_minus_one =
        !input_array.is_null() &&
        slice_end_offset.int64_value() == (input_array.num_elements() - 1);
    bool is_suffix = is_suffix_by_end_neg_one || is_suffix_by_end_len_minus_one;
    if (!is_suffix) {
      continue;  // Skip test cases that are not suffixes.
    }
    if (slice_start_offset.is_null()) {
      // This is a useful test case with a null length argument.
      test_cases.push_back({.array_slice_test = slice_test});
      continue;
    }
    int64_t start_offset = slice_start_offset.int64_value();
    if (start_offset >= 0) {
      // The ARRAY_SLICE function allows positive offsets to mean the offset
      // from the front of the array. For suffix cases, we need the offset from
      // the end, which means we need to adjust by the array length.
      if (input_array.is_null()) {
        continue;  // Skip test that can't be translated into offset from end.
      }
      start_offset -= input_array.num_elements();
    }
    int64_t length = -start_offset;
    test_cases.push_back({.array_slice_test = slice_test, .length = length});
  }
  return test_cases;
}

std::vector<QueryParamsWithResult> GetFunctionTestsArrayFirstN(bool is_safe) {
  std::vector<QueryParamsWithResult> test_cases =
      GetArrayFirstNLastNDesignDocTests(is_safe, kArrayFirstN);

  const absl::StatusCode kErrorCode =
      is_safe ? absl::StatusCode::kOk : absl::StatusCode::kOutOfRange;

  for (const auto& prefix_case : GetPrefixCasesFromArraySlice(is_safe)) {
    const Type* result_type = prefix_case.result().type();
    if (!prefix_case.length.has_value()) {
      test_cases.push_back(
          QueryParamsWithResult({prefix_case.input_arr(), Value::NullInt64()},
                                Value::Null(result_type)));
    } else {
      Value length_arg = Value::Int64(prefix_case.length.value());
      if (length_arg.int64_value() < 0) {
        test_cases.push_back(
            QueryParamsWithResult({prefix_case.input_arr(), length_arg},
                                  Value::Null(result_type), kErrorCode));
      } else {
        test_cases.push_back(QueryParamsWithResult(
            {prefix_case.input_arr(), length_arg}, prefix_case.result()));
      }
    }
    prefix_case.CopyFeaturesTo(test_cases.back());
  }
  for (auto& test_case : test_cases) {
    test_case.AddRequiredFeature(FEATURE_FIRST_AND_LAST_N);
  }
  return test_cases;
}

std::vector<QueryParamsWithResult> GetFunctionTestsArrayLastN(bool is_safe) {
  std::vector<QueryParamsWithResult> test_cases =
      GetArrayFirstNLastNDesignDocTests(is_safe, kArrayLastN);

  const absl::StatusCode kErrorCode =
      is_safe ? absl::StatusCode::kOk : absl::StatusCode::kOutOfRange;

  for (const auto& suffix_case : GetSuffixCasesFromArraySlice(is_safe)) {
    const Type* result_type = suffix_case.result().type();
    if (!suffix_case.length.has_value()) {
      test_cases.push_back(
          QueryParamsWithResult({suffix_case.input_arr(), Value::NullInt64()},
                                Value::Null(result_type)));
    } else {
      Value length_arg = Value::Int64(suffix_case.length.value());
      if (length_arg.int64_value() < 0) {
        test_cases.push_back(
            QueryParamsWithResult({suffix_case.input_arr(), length_arg},
                                  Value::Null(result_type), kErrorCode));
      } else {
        test_cases.push_back(QueryParamsWithResult(
            {suffix_case.input_arr(), length_arg}, suffix_case.result()));
      }
    }
    suffix_case.CopyFeaturesTo(test_cases.back());
  }
  for (auto& test_case : test_cases) {
    test_case.AddRequiredFeature(FEATURE_FIRST_AND_LAST_N);
  }
  return test_cases;
}

std::vector<QueryParamsWithResult> GetFunctionTestsArrayRemoveFirstN(
    bool is_safe) {
  std::vector<QueryParamsWithResult> test_cases =
      GetArrayFirstNLastNDesignDocTests(is_safe, kArrayRemoveFirstN);

  const absl::StatusCode kErrorCode =
      is_safe ? absl::StatusCode::kOk : absl::StatusCode::kOutOfRange;

  for (const auto& suffix_case : GetSuffixCasesFromArraySlice(is_safe)) {
    const Type* result_type = suffix_case.result().type();
    if (!suffix_case.length.has_value()) {
      test_cases.push_back(
          QueryParamsWithResult({suffix_case.input_arr(), Value::NullInt64()},
                                Value::Null(result_type)));
    } else {
      if (suffix_case.input_arr().is_null()) {
        // We know the length of the suffix that ARRAY_SLICE requested, but we
        // can't turn that into a length of suffix to remove without knowing
        // the array length. Skip this test.
        continue;
      }
      Value length_arg = Value::Int64(suffix_case.input_arr().num_elements() -
                                      suffix_case.length.value());
      if (length_arg.int64_value() < 0) {
        test_cases.push_back(
            QueryParamsWithResult({suffix_case.input_arr(), length_arg},
                                  Value::Null(result_type), kErrorCode));
      } else {
        test_cases.push_back(QueryParamsWithResult(
            {suffix_case.input_arr(), length_arg}, suffix_case.result()));
      }
    }
    suffix_case.CopyFeaturesTo(test_cases.back());
  }
  for (auto& test_case : test_cases) {
    test_case.AddRequiredFeature(FEATURE_FIRST_AND_LAST_N);
  }
  return test_cases;
}

std::vector<QueryParamsWithResult> GetFunctionTestsArrayRemoveLastN(
    bool is_safe) {
  std::vector<QueryParamsWithResult> test_cases =
      GetArrayFirstNLastNDesignDocTests(is_safe, kArrayRemoveLastN);

  const absl::StatusCode kErrorCode =
      is_safe ? absl::StatusCode::kOk : absl::StatusCode::kOutOfRange;

  for (const auto& prefix_case : GetPrefixCasesFromArraySlice(is_safe)) {
    const Type* result_type = prefix_case.result().type();
    if (!prefix_case.length.has_value()) {
      test_cases.push_back(
          QueryParamsWithResult({prefix_case.input_arr(), Value::NullInt64()},
                                Value::Null(result_type)));
    } else {
      if (prefix_case.input_arr().is_null()) {
        // We know the length of the prefix that ARRAY_SLICE requested, but we
        // can't turn that into a length of suffix to remove without knowing
        // the array length. Skip this test.
        continue;
      }
      Value length_arg = Value::Int64(prefix_case.input_arr().num_elements() -
                                      prefix_case.length.value());
      if (length_arg.int64_value() < 0) {
        test_cases.push_back(
            QueryParamsWithResult({prefix_case.input_arr(), length_arg},
                                  Value::Null(result_type), kErrorCode));
      } else {
        test_cases.push_back(QueryParamsWithResult(
            {prefix_case.input_arr(), length_arg}, prefix_case.result()));
      }
    }
    prefix_case.CopyFeaturesTo(test_cases.back());
  }
  for (auto& test_case : test_cases) {
    test_case.AddRequiredFeature(FEATURE_FIRST_AND_LAST_N);
  }
  return test_cases;
}

static const std::vector<TypeFeaturePair>
GetOrderableTypesWithFeaturesAndValues() {
  // All type-feature pairs have 4 example inputs.
  // example_input_1 to example_input_4 are designed to be in ascending order.
  // example_input_2 and example_input_3 have equal values.
  return {
      {FloatType(), Float(float_neg_inf), Float(-3.3), Float(-3.3),
       Float(float_pos_inf)},
      {DoubleType(), Double(double_neg_inf), Double(1.2), Double(1.2),
       Double(double_pos_inf)},
      {Int32Type(), Int32(int32min), Int32(1), Int32(1), Int32(int32max)},
      {Int64Type(), Int64(int64min), Int64(-3), Int64(-3), Int64(int64max)},
      {Uint32Type(), Uint32(0), Uint32(0x7FFFFFFF), Uint32(0x7FFFFFFF),
       Uint32(uint32max)},
      {Uint64Type(), Uint64(0), Uint64(-3), Uint64(-3), Uint64(uint64max)},
      {BoolType(), Bool(false), Bool(true), Bool(true), Bool(true)},
      {StringType(), String(""), String("A"), String("A"), String("a")},
      {BytesType(), Bytes("0x00"), Bytes("0xAA"), Bytes("0xAA"), Bytes("0xFF")},
      {TimestampType(), Timestamp(timestamp_min), Timestamp(1500000000),
       Timestamp(1500000000), Timestamp(timestamp_max)},
      {TimestampType(), Timestamp(absl::FromUnixNanos(15674896748561)),
       Timestamp(absl::FromUnixNanos(15674896748562)),
       Timestamp(absl::FromUnixNanos(15674896748562)),
       Timestamp(absl::FromUnixNanos(15674896748563)), FEATURE_TIMESTAMP_NANOS},
      {DateType(), Date(date_min), DateFromStr("1960-01-07"),
       DateFromStr("1960-01-07"), Date(date_max)},
      {NumericType(), Numeric(NumericValue::MinValue()), Numeric(-3),
       Numeric(-3), Numeric(NumericValue::MaxValue()), FEATURE_NUMERIC_TYPE},
      {BigNumericType(), BigNumeric(BigNumericValue::MinValue()),
       BigNumeric(BigNumericValue::FromStringStrict("123.456").value()),
       BigNumeric(BigNumericValue::FromStringStrict("123.456").value()),
       BigNumeric(BigNumericValue::MaxValue()), FEATURE_BIGNUMERIC_TYPE},
      {IntervalType(), Interval(IntervalValue::MinValue()),
       Interval(IntervalValue::FromDays(30).value()),
       Interval(IntervalValue::FromMonths(1).value()),
       Interval(IntervalValue::MaxValue()), FEATURE_INTERVAL_TYPE},
      {UuidType(), Uuid(UuidValue::MinValue()),
       Uuid(UuidValue::FromString("9d3da323-4c20-360f-bd9b-ec54feec54f0")
                .value()),
       Uuid(UuidValue::FromString("9d3da323-4c20-360f-bd9b-ec54feec54f0")
                .value()),
       Uuid(UuidValue::MaxValue()), FEATURE_UUID_TYPE},
      {TimeType(), TimeMicros(0, 0, 0, 0), TimeMicros(1, 2, 3, 4),
       TimeMicros(1, 2, 3, 4), TimeMicros(1, 2, 3, 123450), FEATURE_CIVIL_TIME},
      {DatetimeType(), DatetimeMicros(2006, 1, 2, 3, 4, 5, 123456),
       DatetimeMicros(2022, 1, 2, 3, 4, 5, 123456),
       DatetimeMicros(2022, 1, 2, 3, 4, 5, 123456),
       DatetimeMicros(2022, 12, 24, 1, 2, 3, 123456), FEATURE_CIVIL_TIME},
      // Enum
      {
          TestEnumType(),
          Value::Enum(TestEnumType(), 0),
          Value::Enum(TestEnumType(), 1),
          Value::Enum(TestEnumType(), 1),
          Value::Enum(TestEnumType(), 0x000000002),
      },
      // Ranges
      {
          types::DateRangeType(),
          Range(NullDate(), Date(1)),
          Range(Date(1), Date(2)),
          Range(Date(1), Date(2)),
          Range(Date(5), Date(10)),
          FEATURE_RANGE_TYPE,
      },
      {types::DatetimeRangeType(),
       Range(NullDatetime(), Datetime(DatetimeValue::FromYMDHMSAndMicros(
                                 2024, 1, 9, 10, 00, 00, 99))),
       Range(Datetime(DatetimeValue::FromYMDHMSAndMicros(2024, 1, 9, 12, 40, 55,
                                                         99)),
             Datetime(DatetimeValue::FromYMDHMSAndMicros(2024, 1, 9, 17, 40, 56,
                                                         99))),
       Range(Datetime(DatetimeValue::FromYMDHMSAndMicros(2024, 1, 9, 12, 40, 55,
                                                         99)),
             Datetime(DatetimeValue::FromYMDHMSAndMicros(2024, 1, 9, 17, 40, 56,
                                                         99))),
       Range(Datetime(
                 DatetimeValue::FromYMDHMSAndMicros(2024, 1, 20, 10, 0, 0, 0)),
             NullDatetime()),
       {FEATURE_RANGE_TYPE, FEATURE_CIVIL_TIME}},
      {
          types::TimestampRangeType(),
          Range(NullTimestamp(), Timestamp(2)),
          Range(Timestamp(5), Timestamp(10)),
          Range(Timestamp(5), Timestamp(10)),
          Range(Timestamp(20), Timestamp(50)),
          FEATURE_RANGE_TYPE,
      },
  };
}

static const std::vector<QueryParamsWithResult> GetArrayMinMaxTestCases(
    bool is_safe, bool is_min) {
  std::vector<QueryParamsWithResult> test_cases;
  std::vector<TypeFeaturePair> pairs = GetOrderableTypesWithFeaturesAndValues();
  ABSL_DCHECK_GT(pairs.size(), 0);
  for (const TypeFeaturePair& v : pairs) {
    // Setup array values with different example input combinations, which are
    // used as input of the test cases.
    ABSL_DCHECK(v.example_input_4.is_valid());
    const ArrayType* array_type = MakeArrayType(v.type, type_factory());
    Value null = Null(v.type);
    Value input1234 =
        values::Array(array_type, {v.example_input_1, v.example_input_2,
                                   v.example_input_3, v.example_input_4});
    Value input4132 =
        values::Array(array_type, {v.example_input_4, v.example_input_1,
                                   v.example_input_3, v.example_input_2});

    Value input234 = values::Array(
        array_type, {v.example_input_2, v.example_input_3, v.example_input_4});
    Value input342 = values::Array(
        array_type, {v.example_input_3, v.example_input_4, v.example_input_2});
    Value input123 = values::Array(
        array_type, {v.example_input_1, v.example_input_2, v.example_input_3});
    Value input321 = values::Array(
        array_type, {v.example_input_3, v.example_input_2, v.example_input_1});

    Value input23 =
        values::Array(array_type, {v.example_input_2, v.example_input_3});
    Value input32 =
        values::Array(array_type, {v.example_input_3, v.example_input_2});
    Value input2 = values::Array(array_type, {v.example_input_2});

    Value input_nulls = values::Array(array_type, {null, null});
    Value input_null_4132 =
        values::Array(array_type, {null, v.example_input_4, v.example_input_1,
                                   v.example_input_3, v.example_input_2});
    Value input_234_null = values::Array(
        array_type,
        {v.example_input_2, v.example_input_3, v.example_input_4, null});
    Value input_342_null = values::Array(
        array_type,
        {v.example_input_3, v.example_input_4, v.example_input_2, null});
    Value input_123_null = values::Array(
        array_type,
        {v.example_input_1, v.example_input_2, v.example_input_3, null});
    Value input_321_null = values::Array(
        array_type,
        {v.example_input_3, v.example_input_2, v.example_input_1, null});
    Value input_2_null = values::Array(array_type, {v.example_input_2, null});

    size_t existing_num_tests = test_cases.size();
    // 1. Empty array argument.
    test_cases.push_back(
        QueryParamsWithResult({Value::EmptyArray(array_type)}, null));

    // 2. NULL array argument.
    test_cases.push_back(
        QueryParamsWithResult({Value::Null(array_type)}, null));

    // 3. Array argument with all NULL elements.
    test_cases.push_back(QueryParamsWithResult({input_nulls}, null));

    // 4. When array has singleton value, returns the same value.
    test_cases.push_back(QueryParamsWithResult({input2}, v.example_input_2));

    test_cases.push_back(
        QueryParamsWithResult({input_2_null}, v.example_input_2));

    if (is_min) {
      // Test cases for ARRAY_MIN
      // 5. Array argument with non-NULL elements, returns the smallest.
      // 5.1 When the smallest element does not have tie, returns the smallest.
      test_cases.push_back(
          QueryParamsWithResult({input1234}, v.example_input_1));

      test_cases.push_back(
          QueryParamsWithResult({input4132}, v.example_input_1));

      // 5.2 When the smallest elements have ties, returns the one with smaller
      // offset.
      test_cases.push_back(
          QueryParamsWithResult({input234}, v.example_input_2));

      test_cases.push_back(
          QueryParamsWithResult({input342}, v.example_input_3));

      test_cases.push_back(QueryParamsWithResult({input23}, v.example_input_2));

      test_cases.push_back(QueryParamsWithResult({input32}, v.example_input_3));

      // 6. Array argument with NULL and non-NULL elements, returns the smallest
      // among non-NULL elements.
      // 6.1 When the smallest element does not have tie, returns the smallest.
      test_cases.push_back(
          QueryParamsWithResult({input_null_4132}, v.example_input_1));

      // 6.2 When the smallest elements have ties, returns the one with smaller
      // offset.
      test_cases.push_back(
          QueryParamsWithResult({input_342_null}, v.example_input_3));

      test_cases.push_back(
          QueryParamsWithResult({input_234_null}, v.example_input_2));
    } else {
      // Test cases for ARRAY_MAX
      // 5. Array argument with non-NULL elements, returns the largest.
      // 5.1 When the largest element does not have tie, returns the largest.
      test_cases.push_back(
          QueryParamsWithResult({input1234}, v.example_input_4));

      test_cases.push_back(
          QueryParamsWithResult({input4132}, v.example_input_4));

      // 5.2 When the largest elements have ties, returns the one with smaller
      // offset.
      test_cases.push_back(
          QueryParamsWithResult({input123}, v.example_input_2));

      test_cases.push_back(
          QueryParamsWithResult({input321}, v.example_input_3));

      test_cases.push_back(QueryParamsWithResult({input23}, v.example_input_2));

      test_cases.push_back(QueryParamsWithResult({input32}, v.example_input_3));

      // 6. Array argument with NULL and non-NULL elements, returns the largest
      // among non-NULL elements.
      // 6.1 When the largest element does not have tie, returns the largest.
      test_cases.push_back(
          QueryParamsWithResult({input_null_4132}, v.example_input_4));

      // 6.2 When the largest elements have ties, returns the one with smaller
      // offset.
      test_cases.push_back(
          QueryParamsWithResult({input_123_null}, v.example_input_2));

      test_cases.push_back(
          QueryParamsWithResult({input_321_null}, v.example_input_3));
    }

    // 7. Floating point array argument with NAN element saturation.
    if (v.type->IsFloatingPoint()) {
      Value nan = v.type->IsFloat() ? Value::Float(float_nan)
                                    : Value::Double(double_nan);
      // 7.1 Array argument with non-NULL elements
      test_cases.push_back(QueryParamsWithResult(
          {values::Array(array_type,
                         {v.example_input_2, v.example_input_3, nan})},
          nan));

      // 7.2 Array argument with NULL and non-NULL elements
      test_cases.push_back(QueryParamsWithResult(
          {values::Array(array_type,
                         {null, v.example_input_2, v.example_input_3, nan})},
          nan));
    }
    for (size_t i = existing_num_tests; i < test_cases.size(); ++i) {
      if (is_safe) {
        test_cases[i].AddRequiredFeature(FEATURE_SAFE_FUNCTION_CALL);
      }

      std::set<LanguageFeature> required_features = v.required_features;
      if (test_cases[i].result().is_null() ||
          test_cases[i].result().type()->IsTimestamp()) {
        // TIMESTAMP_NANOS feature doesn't affect null results or ARRAY_MIN/MAX
        // of TIMESTAMP.
        required_features.erase(FEATURE_TIMESTAMP_NANOS);
      }

      test_cases[i]
          .AddRequiredFeatures(required_features)
          .AddProhibitedFeature(FEATURE_DISABLE_ARRAY_MIN_AND_MAX);
    }
  }
  return test_cases;
}

struct ArraySumAvgTestCase {
  ArraySumAvgTestCase(const Type* type, const Value& min, const Value& example1,
                      const Value& example2, const Value& max,
                      const Value& output_sum, const Value& output_avg)
      : type(type),
        min_input(min),
        example_input_1(example1),
        example_input_2(example2),
        max_input(max),
        example_output_sum(output_sum),
        example_output_avg(output_avg),
        required_features({}) {}

  ArraySumAvgTestCase(const Type* type, const Value& min, const Value& example1,
                      const Value& example2, const Value& max,
                      const Value& output_sum, const Value& output_avg,
                      LanguageFeature feature)
      : type(type),
        min_input(min),
        example_input_1(example1),
        example_input_2(example2),
        max_input(max),
        example_output_sum(output_sum),
        example_output_avg(output_avg),
        required_features({feature}) {}

  // Element type of array argument of the test cases. The type has to be either
  // numerical or interval type.
  const Type* type;
  Value min_input;
  Value example_input_1;
  Value example_input_2;
  Value max_input;
  Value example_output_sum;
  Value example_output_avg;
  std::set<LanguageFeature> required_features;
};

// Generate element types, required features, and example values.
// They will be used as test cases for ARRAY_SUM and ARRAY_AVG. Example
// values include two extreme values (min and max), two non-overflowing values
// whose sum is also non-overflowing, and the sum and avg for the two
// non-overflowing values.
// Note that, it is expected to see undeterminism in floating point summation
// and average. The values here are added for reference purpose.
static const std::vector<ArraySumAvgTestCase>
GetSumAvgNumericalAndIntervalTypesWithFeatures() {
  return {
      {FloatType(), Float(floatmin), Float(-1.2), Float(3.3), Float(floatmax),
       Double(2.1), Double(1.05)},
      {DoubleType(), Double(doublemin), Double(10.2), Double(-13.3),
       Double(doublemax), Double(-3.1), Double(-1.55)},
      {Int32Type(), Int32(int32min), Int32(-9), Int32(4), Int32(int32max),
       Int64(-5), Double(-2.5)},
      {Int64Type(), Int64(int64min), Int64(int32min_minus_one),
       Int64(int32max_plus_one), Int64(int64max), Int64(-1), Double(-0.5)},
      {Uint32Type(), Uint32(0), Uint32(1), Uint32(4), Uint32(uint32max),
       Uint64(5), Double(2.5)},
      {Uint64Type(), Uint64(0), Uint64(0x7FFFFFFFFFFFFFFF),
       Uint64(0x7FFFFFFFFFFFFFFF), Uint64(uint64max),
       Uint64(0xFFFFFFFFFFFFFFFE), Double(0x7FFFFFFFFFFFFFFF)},
      {NumericType(), Numeric(NumericValue::MinValue()), Numeric(3), Numeric(3),
       Numeric(NumericValue::MaxValue()), Numeric(6), Numeric(3),
       FEATURE_NUMERIC_TYPE},
      {BigNumericType(), BigNumeric(BigNumericValue::MinValue()),
       BigNumeric(BigNumericValue::FromStringStrict("123.5").value()),
       BigNumeric(BigNumericValue::FromStringStrict("124").value()),
       BigNumeric(BigNumericValue::MaxValue()),
       BigNumeric(BigNumericValue::FromStringStrict("247.5").value()),
       BigNumeric(BigNumericValue::FromStringStrict("123.75").value()),
       FEATURE_BIGNUMERIC_TYPE},
      {IntervalType(), Interval(IntervalValue::MinValue()),
       Interval(IntervalValue::FromDays(30).value()),
       Interval(IntervalValue::FromMonths(1).value()),
       Interval(IntervalValue::MaxValue()),
       Interval(IntervalValue::FromMonthsDaysMicros(1, 30, 0).value()),
       Interval(IntervalValue::FromDays(30).value()), FEATURE_INTERVAL_TYPE}};
}

static const std::vector<QueryParamsWithResult> GetArraySumTestCases(
    bool is_safe) {
  Value twice_int32_max = Value::Int64(static_cast<int64_t>(int32max) +
                                       static_cast<int64_t>(int32max));
  Value twice_int32_min = Value::Int64(static_cast<int64_t>(int32min) +
                                       static_cast<int64_t>(int32min));
  Value twice_uint32_max = Value::Uint64(static_cast<uint64_t>(uint32max) +
                                         static_cast<uint64_t>(uint32max));
  Value twice_float_max = Value::Double(static_cast<double>(floatmax) +
                                        static_cast<double>(floatmax));
  Value twice_float_min = Value::Double(static_cast<double>(floatmin) +
                                        static_cast<double>(floatmin));

  std::vector<QueryParamsWithResult> test_cases;
  std::vector<ArraySumAvgTestCase> raw_test_cases =
      GetSumAvgNumericalAndIntervalTypesWithFeatures();

  for (const ArraySumAvgTestCase& v : raw_test_cases) {
    // Set up values with superset types for INT32, UINT32 and FLOAT.
    Value input_null = Null(v.type);
    Value output_null = input_null;
    Value output_1 = v.example_input_1;
    switch (v.type->kind()) {
      case TYPE_INT32:
        output_null = NullInt64();
        output_1 = Value::Int64(v.example_input_1.ToInt64());
        break;
      case TYPE_UINT32:
        output_null = NullUint64();
        output_1 = Value::Uint64(v.example_input_1.ToUint64());
        break;
      case TYPE_FLOAT:
        output_null = NullDouble();
        output_1 = Value::Double(v.example_input_1.ToDouble());
        break;
      default:
        break;
    }
    EXPECT_EQ(output_null.type()->kind(), v.example_output_sum.type()->kind());

    // Setup array values with different example inputs. They will be used as
    // input of the test cases.
    const ArrayType* array_type = MakeArrayType(v.type, type_factory());
    Value input1 = values::Array(array_type, {v.example_input_1});
    Value input_1_null =
        values::Array(array_type, {v.example_input_1, input_null});
    Value input12 =
        values::Array(array_type, {v.example_input_1, v.example_input_2});
    Value input_12_null = values::Array(
        array_type, {v.example_input_1, v.example_input_2, input_null});
    Value input_max_max = values::Array(array_type, {v.max_input, v.max_input});
    Value input_max_max_null =
        values::Array(array_type, {v.max_input, v.max_input, input_null});
    Value input_min_min = values::Array(array_type, {v.min_input, v.min_input});
    Value input_min_min_null =
        values::Array(array_type, {v.min_input, v.min_input, input_null});
    Value input_nulls = values::Array(array_type, {input_null, input_null});

    size_t existing_num_tests = test_cases.size();
    // 1. Empty array argument.
    test_cases.push_back(
        QueryParamsWithResult({Value::EmptyArray(array_type)}, output_null));

    // 2. NULL array argument.
    test_cases.push_back(
        QueryParamsWithResult({Value::Null(array_type)}, output_null));

    // 3. Array argument with all NULL elements.
    test_cases.push_back(QueryParamsWithResult({input_nulls}, output_null));

    // 4. Array argument with non-extreme elements.
    if (!v.type->IsFloatingPoint()) {
      // 4.1 For non-floating point element types, the result comparison uses
      // exact matching floating point margin kExactFloatMargin by default.
      // 4.1.1 Non-extreme elements without NULL.
      test_cases.push_back(QueryParamsWithResult({input1}, output_1));

      test_cases.push_back(
          QueryParamsWithResult({input12}, v.example_output_sum));

      // 4.1.2 Non-extreme elements with NULL.
      test_cases.push_back(QueryParamsWithResult({input_1_null}, output_1));

      test_cases.push_back(
          QueryParamsWithResult({input_12_null}, v.example_output_sum));
    } else {
      // 4.2 For floating point element types, the result comparison uses
      // non-precise floating point margin setting with the lowest possible ULP.
      FloatMargin margin = FloatMargin::UlpMargin(28);
      // 4.2.1 Non-extreme elements without NULL.
      test_cases.push_back(QueryParamsWithResult({input1}, output_1, margin));

      test_cases.push_back(
          QueryParamsWithResult({input12}, v.example_output_sum, margin));

      // 4.2.2 Non-extreme elements with NULL.
      test_cases.push_back(
          QueryParamsWithResult({input_1_null}, output_1, margin));

      test_cases.push_back(
          QueryParamsWithResult({input_12_null}, v.example_output_sum, margin));
    }

    // 5. Array argument produces overflow in the positive range sometimes.
    if (v.type->IsInt32() || v.type->IsUint32() || v.type->IsFloat()) {
      // 5.1 For array argument with element type INT32, UINT32, or FLOAT,
      // adding not enough positive extreme elements DOES NOT produce overflow.
      Value output_two_max = twice_int32_max;
      if (v.type->IsUint32()) {
        output_two_max = twice_uint32_max;
      } else if (v.type->IsFloat()) {
        output_two_max = twice_float_max;
      }

      // 5.1.1 Upper-bound extreme elements without NULL.
      test_cases.push_back(
          QueryParamsWithResult({input_max_max}, output_two_max));

      // 5.1.2 Upper-bound extreme elements with NULL.
      test_cases.push_back(
          QueryParamsWithResult({input_max_max_null}, output_two_max));
    } else {
      // 5.2 For array argument with element type INT64, UINT64, DOUBLE,
      // NUMERIC, BIGNUMERIC, or INTERVAL, adding positive extreme elements DOES
      // easily produce positive overflow.
      // 5.2.1 Upper-bound extreme elements without NULL.
      test_cases.push_back(QueryParamsWithResult({input_max_max}, output_null,
                                                 is_safe ? OK : OUT_OF_RANGE));

      // 5.2.2 Upper-bound extreme elements with NULL.
      test_cases.push_back(QueryParamsWithResult(
          {input_max_max_null}, output_null, is_safe ? OK : OUT_OF_RANGE));
    }

    // 6. Array argument produces overflow in the negative range sometimes.
    // Note that, UINT32 and UINT64 are not tested here since the minimum are 0.
    if (v.type->IsInt32() || v.type->IsFloat()) {
      // 6.1 For array argument with element type INT32 or FLOAT, adding not
      // enough negative extreme elements DOES NOT produce negative overflow.
      Value output_two_min = twice_int32_min;
      if (v.type->IsFloat()) {
        output_two_min = twice_float_min;
      }

      // 6.1.1 Lower-bound extreme elements without NULL.
      test_cases.push_back(
          QueryParamsWithResult({input_min_min}, output_two_min));

      // 6.1.2 Lower-bound extreme elements with NULL.
      test_cases.push_back(
          QueryParamsWithResult({input_min_min_null}, output_two_min));
    } else if (v.type->IsInt64() || v.type->IsDouble() ||
               v.type->IsNumericType() || v.type->IsBigNumericType() ||
               v.type->IsInterval()) {
      // 6.2 For array argument with element type INT64, DOUBLE, NUMERIC,
      // BIGNUMERIC, or INTERVAL, adding negative extreme elements DOES easily
      // produce negative overflow.
      // 6.2.1 Lower-bound extreme elements without NULL.
      test_cases.push_back(QueryParamsWithResult({input_min_min}, output_null,
                                                 is_safe ? OK : OUT_OF_RANGE));

      // 6.2.2 Lower-bound extreme elements with NULL.
      test_cases.push_back(QueryParamsWithResult(
          {input_min_min_null}, output_null, is_safe ? OK : OUT_OF_RANGE));
    }

    // 7. Floating point array argument has special rules.
    if (v.type->IsFloatingPoint()) {
      Value input_nan = v.type->IsFloat() ? Value::Float(float_nan)
                                          : Value::Double(double_nan);
      Value output_nan = Value::Double(double_nan);
      Value input_pos_inf = v.type->IsFloat() ? Value::Float(float_pos_inf)
                                              : Value::Double(double_pos_inf);
      Value output_pos_inf = Value::Double(double_pos_inf);
      Value input_neg_inf = v.type->IsFloat() ? Value::Float(float_neg_inf)
                                              : Value::Double(double_neg_inf);
      Value output_neg_inf = Value::Double(double_neg_inf);

      // 7.1 FP elements containing +inf will produce +inf.
      test_cases.push_back(QueryParamsWithResult(
          {values::Array(array_type, {input_pos_inf, v.example_input_1,
                                      v.example_input_2})},
          output_pos_inf));

      // 7.2 FP elements containing +inf and NULL will produce +inf.
      test_cases.push_back(QueryParamsWithResult(
          {values::Array(array_type, {input_pos_inf, v.example_input_1,
                                      v.example_input_2, input_null})},
          output_pos_inf));

      // 7.3 FP elements containing -inf and NULL will produce -inf.
      test_cases.push_back(QueryParamsWithResult(
          {values::Array(array_type, {input_neg_inf, v.example_input_1,
                                      v.example_input_2})},
          output_neg_inf));

      // 7.4 FP elements containing -inf and NULL will produce -inf.
      test_cases.push_back(QueryParamsWithResult(
          {values::Array(array_type, {input_neg_inf, v.example_input_1,
                                      v.example_input_2, input_null})},
          output_neg_inf));

      // 7.5 FP elements containing +inf and -inf will produce NAN.
      test_cases.push_back(QueryParamsWithResult(
          {values::Array(array_type, {input_neg_inf, input_pos_inf,
                                      v.example_input_1, v.example_input_2})},
          output_nan));

      // 7.6 FP elements containing +inf, -inf and NULL will produce NAN.
      test_cases.push_back(QueryParamsWithResult(
          {values::Array(array_type,
                         {input_neg_inf, input_pos_inf, v.example_input_1,
                          v.example_input_2, input_null})},
          output_nan));

      // 7.7 FP elements containing NAN will produce NAN.
      test_cases.push_back(QueryParamsWithResult(
          {values::Array(array_type,
                         {input_nan, v.example_input_1, v.example_input_2})},
          output_nan));

      // 7.8 FP elements containing NAN and NULL will produce NAN.
      test_cases.push_back(QueryParamsWithResult(
          {values::Array(array_type, {input_nan, v.example_input_1,
                                      v.example_input_2, input_null})},
          output_nan));

      // 7.9 FP elements containing NAN, -inf and +inf will produce NAN.
      test_cases.push_back(QueryParamsWithResult(
          {values::Array(array_type, {input_nan, input_pos_inf, input_neg_inf,
                                      v.example_input_1, v.example_input_2})},
          output_nan));
    }

    for (size_t i = existing_num_tests; i < test_cases.size(); ++i) {
      if (is_safe) {
        test_cases[i].AddRequiredFeature(FEATURE_SAFE_FUNCTION_CALL);
      }
      QueryParamsWithResult::FeatureSet feature_set;
      feature_set.insert(FEATURE_ARRAY_AGGREGATION_FUNCTIONS);
      if (!v.required_features.empty()) {
        for (const LanguageFeature& feature : v.required_features) {
          feature_set.insert(feature);
        }
      }
      test_cases[i].AddRequiredFeatures(feature_set);
    }
  }
  return test_cases;
}

static const std::vector<QueryParamsWithResult> GetArrayAvgTestCases(
    bool is_safe) {
  Value int32_zero = Int32(0);
  Value int64_zero = Int64(0);
  Value uint32_zero = Uint32(0);
  Value uint64_zero = Uint64(0);
  Value float_zero = Float(0);
  Value double_zero = Double(0);
  Value numeric_zero = Numeric(0);
  Value bignumeric_zero = BigNumeric(0);
  Value interval_zero = Value::Interval(IntervalValue::FromDays(0).value());
  Value double_null = Value::NullDouble();

  std::vector<QueryParamsWithResult> test_cases;
  std::vector<ArraySumAvgTestCase> raw_test_cases =
      GetSumAvgNumericalAndIntervalTypesWithFeatures();

  for (const ArraySumAvgTestCase& v : raw_test_cases) {
    // Set up frequently used values with different example inputs.
    // They will be used as input of the test cases.
    const ArrayType* array_type = MakeArrayType(v.type, type_factory());
    Value input_null = Null(v.type);
    Value input_zero;
    Value output_null;
    Value output_zero;
    Value output_1;
    Value output_min;
    Value output_max;

    switch (v.type->kind()) {
      case TYPE_NUMERIC:
        input_zero = values::Array(array_type, {numeric_zero});
        output_null = Value::NullNumeric();
        output_zero = numeric_zero;
        output_1 = v.example_input_1;
        output_min = v.min_input;
        output_max = v.max_input;
        break;
      case TYPE_BIGNUMERIC:
        input_zero = values::Array(array_type, {bignumeric_zero});
        output_null = Value::NullBigNumeric();
        output_zero = bignumeric_zero;
        output_1 = v.example_input_1;
        output_min = v.min_input;
        output_max = v.max_input;
        break;
      case TYPE_INTERVAL:
        input_zero = values::Array(array_type, {interval_zero});
        output_null = Value::NullInterval();
        output_zero = interval_zero;
        output_1 = v.example_input_1;
        output_min = v.min_input;
        output_max = v.max_input;
        break;
      case TYPE_INT32:
        input_zero = values::Array(array_type, {int32_zero});
        output_null = double_null;
        output_zero = double_zero;
        output_1 = Value::Double(v.example_input_1.ToDouble());
        output_min = Value::Double(v.min_input.ToDouble());
        output_max = Value::Double(v.max_input.ToDouble());
        break;
      case TYPE_INT64:
        input_zero = values::Array(array_type, {int64_zero});
        output_null = double_null;
        output_zero = double_zero;
        output_1 = Value::Double(v.example_input_1.ToDouble());
        output_min = Value::Double(v.min_input.ToDouble());
        output_max = Value::Double(v.max_input.ToDouble());
        break;
      case TYPE_UINT32:
        input_zero = values::Array(array_type, {uint32_zero});
        output_null = double_null;
        output_zero = double_zero;
        output_1 = Value::Double(v.example_input_1.ToDouble());
        output_min = Value::Double(v.min_input.ToDouble());
        output_max = Value::Double(v.max_input.ToDouble());
        break;
      case TYPE_UINT64:
        input_zero = values::Array(array_type, {uint64_zero});
        output_null = double_null;
        output_zero = double_zero;
        output_1 = Value::Double(v.example_input_1.ToDouble());
        output_min = Value::Double(v.min_input.ToDouble());
        output_max = Value::Double(v.max_input.ToDouble());
        break;
      case TYPE_FLOAT:
        input_zero = values::Array(array_type, {float_zero});
        output_null = double_null;
        output_zero = double_zero;
        output_1 = Value::Double(v.example_input_1.ToDouble());
        output_min = Value::Double(v.min_input.ToDouble());
        output_max = Value::Double(v.max_input.ToDouble());
        break;
      case TYPE_DOUBLE:
        input_zero = values::Array(array_type, {double_zero});
        output_null = double_null;
        output_zero = double_zero;
        output_1 = Value::Double(v.example_input_1.ToDouble());
        output_min = Value::Double(v.min_input.ToDouble());
        output_max = Value::Double(v.max_input.ToDouble());
        break;
      default:
        ABSL_CHECK(false)
            << "ARRAY_AVG only supports INT32, INT64, UINT32, UINT64, FLOAT, "
               "DOUBLE, NUMERIC, BIGNUMERIC or INTERVAL array element type";
        break;
    }
    EXPECT_EQ(output_null.type()->kind(), v.example_output_avg.type()->kind());

    Value input1 = values::Array(array_type, {v.example_input_1});
    Value input_1_null =
        values::Array(array_type, {v.example_input_1, input_null});
    Value input_11 =
        values::Array(array_type, {v.example_input_1, v.example_input_1});
    Value input_11_null = values::Array(
        array_type, {v.example_input_1, v.example_input_1, input_null});
    Value input12 =
        values::Array(array_type, {v.example_input_1, v.example_input_2});
    Value input_12_null = values::Array(
        array_type, {v.example_input_1, v.example_input_2, input_null});
    Value input_max_max = values::Array(array_type, {v.max_input, v.max_input});
    Value input_max_max_null =
        values::Array(array_type, {v.max_input, v.max_input, input_null});
    Value input_min_min = values::Array(array_type, {v.min_input, v.min_input});
    Value input_min_min_null =
        values::Array(array_type, {v.min_input, v.min_input, input_null});
    Value input_nulls = values::Array(array_type, {input_null, input_null});

    size_t existing_num_tests = test_cases.size();
    // 1. Empty array argument.
    test_cases.push_back(
        QueryParamsWithResult({Value::EmptyArray(array_type)}, output_null));

    // 2. NULL array argument.
    test_cases.push_back(
        QueryParamsWithResult({Value::Null(array_type)}, output_null));

    // 3. Array argument with all NULL elements.
    test_cases.push_back(QueryParamsWithResult({input_nulls}, output_null));

    // 4. Array argument with element(s) of the same value produces an average
    // of the same singleton value.
    // 4.1 Non-extreme elements.
    // 4.1.1 Array element(s) without NULL.
    test_cases.push_back(QueryParamsWithResult({input1}, output_1));

    test_cases.push_back(QueryParamsWithResult({input_11}, output_1));

    // 4.1.1 Array elements with NULL.
    test_cases.push_back(QueryParamsWithResult({input_1_null}, output_1));

    test_cases.push_back(QueryParamsWithResult({input_11_null}, output_1));

    // 4.2 Extreme elements.
    // 4.2.1 Array element(s) without NULL.
    test_cases.push_back(QueryParamsWithResult({input_min_min}, output_min));

    test_cases.push_back(QueryParamsWithResult({input_max_max}, output_max));

    test_cases.push_back(QueryParamsWithResult({input_zero}, output_zero));

    // 4.2.2 Array elements with NULL.
    test_cases.push_back(
        QueryParamsWithResult({input_min_min_null}, output_min));

    test_cases.push_back(
        QueryParamsWithResult({input_max_max_null}, output_max));

    // 5. Array argument with elements of different values produces
    // indeterministic output, and the result comparison uses FloatMargin. ulp=4
    FloatMargin margin = FloatMargin::UlpMargin(28);
    // 5.1 Elements without NULL.
    test_cases.push_back(
        QueryParamsWithResult({input12}, v.example_output_avg, margin));

    // 5.2 Elements with NULL.
    test_cases.push_back(
        QueryParamsWithResult({input_12_null}, v.example_output_avg, margin));

    // 6. Floating point array argument has special rules.
    if (v.type->IsFloatingPoint()) {
      Value input_nan = v.type->IsFloat() ? Value::Float(float_nan)
                                          : Value::Double(double_nan);
      Value output_nan = Value::Double(double_nan);
      Value input_pos_inf = v.type->IsFloat() ? Value::Float(float_pos_inf)
                                              : Value::Double(double_pos_inf);
      Value output_pos_inf = Value::Double(double_pos_inf);
      Value input_neg_inf = v.type->IsFloat() ? Value::Float(float_neg_inf)
                                              : Value::Double(double_neg_inf);
      Value output_neg_inf = Value::Double(double_neg_inf);

      // 6.1 FP array containing a singleton +/-inf will produce the
      // corresponding +/-inf.
      // 6.1.1 Positive infinity.
      test_cases.push_back(QueryParamsWithResult(
          {values::Array(array_type, {input_pos_inf})}, output_pos_inf));

      test_cases.push_back(QueryParamsWithResult(
          {values::Array(array_type, {input_pos_inf, input_null})},
          output_pos_inf));

      // 6.1.2 Negative infinity.
      test_cases.push_back(QueryParamsWithResult(
          {values::Array(array_type, {input_neg_inf})}, output_neg_inf));

      test_cases.push_back(QueryParamsWithResult(
          {values::Array(array_type, {input_neg_inf, input_null})},
          output_neg_inf));

      // 6.2 FP array containing multiple +/-inf saturates and returns NAN.
      test_cases.push_back(QueryParamsWithResult(
          {values::Array(array_type, {input_pos_inf, input_pos_inf})},
          output_nan));

      test_cases.push_back(QueryParamsWithResult(
          {values::Array(array_type,
                         {input_pos_inf, input_pos_inf, input_null})},
          output_nan));

      test_cases.push_back(QueryParamsWithResult(
          {values::Array(array_type, {input_neg_inf, input_neg_inf})},
          output_nan));

      test_cases.push_back(QueryParamsWithResult(
          {values::Array(array_type,
                         {input_neg_inf, input_neg_inf, input_null})},
          output_nan));

      test_cases.push_back(QueryParamsWithResult(
          {values::Array(array_type, {input_pos_inf, input_neg_inf})},
          output_nan));

      test_cases.push_back(QueryParamsWithResult(
          {values::Array(array_type,
                         {input_pos_inf, input_neg_inf, input_null})},
          output_nan));

      // 6.3 FP array containing NAN saturates and returns NAN.
      test_cases.push_back(QueryParamsWithResult(
          {values::Array(array_type, {input_nan})}, output_nan));

      test_cases.push_back(QueryParamsWithResult(
          {values::Array(array_type, {input_nan, input_null})}, output_nan));

      test_cases.push_back(QueryParamsWithResult(
          {values::Array(array_type, {input_nan, input_nan})}, output_nan));

      test_cases.push_back(QueryParamsWithResult(
          {values::Array(array_type, {input_nan, input_nan, input_null})},
          output_nan));

      test_cases.push_back(QueryParamsWithResult(
          {values::Array(array_type, {input_nan, v.example_input_1})},
          output_nan));

      test_cases.push_back(QueryParamsWithResult(
          {values::Array(array_type,
                         {input_nan, v.example_input_1, input_null})},
          output_nan));

      test_cases.push_back(QueryParamsWithResult(
          {values::Array(array_type, {input_nan, input_pos_inf})}, output_nan));

      test_cases.push_back(QueryParamsWithResult(
          {values::Array(array_type, {input_nan, input_pos_inf, input_null})},
          output_nan));

      test_cases.push_back(QueryParamsWithResult(
          {values::Array(array_type, {input_nan, input_neg_inf})}, output_nan));

      test_cases.push_back(QueryParamsWithResult(
          {values::Array(array_type, {input_nan, input_neg_inf, input_null})},
          output_nan));
    }

    // 7. Test ARRAY_AVG() with double::max() and double::min().
    if (v.type->IsDouble()) {
      test_cases.push_back(QueryParamsWithResult(
          {values::Array(array_type, {v.min_input, v.max_input})},
          output_zero));
      test_cases.push_back(QueryParamsWithResult(
          {values::Array(array_type, {v.min_input, v.min_input, v.min_input})},
          v.min_input));
      test_cases.push_back(QueryParamsWithResult(
          {values::Array(array_type, {v.max_input, v.max_input, v.max_input})},
          v.max_input));

      // With some NULL elements
      test_cases.push_back(QueryParamsWithResult(
          {values::Array(array_type, {v.min_input, v.max_input, input_null})},
          output_zero));
      test_cases.push_back(QueryParamsWithResult(
          {values::Array(array_type,
                         {v.min_input, v.min_input, v.min_input, input_null})},
          v.min_input));
      test_cases.push_back(QueryParamsWithResult(
          {values::Array(array_type,
                         {v.max_input, v.max_input, v.max_input, input_null})},
          v.max_input));
    }

    for (size_t i = existing_num_tests; i < test_cases.size(); ++i) {
      if (is_safe) {
        test_cases[i].AddRequiredFeature(FEATURE_SAFE_FUNCTION_CALL);
      }
      QueryParamsWithResult::FeatureSet feature_set;
      feature_set.insert(FEATURE_ARRAY_AGGREGATION_FUNCTIONS);
      if (!v.required_features.empty()) {
        for (const LanguageFeature& feature : v.required_features) {
          feature_set.insert(feature);
        }
      }
      test_cases[i].AddRequiredFeatures(feature_set);
    }
  }
  return test_cases;
}

struct ArrayFindFunctionsTestCase {
  Value input_array;
  Value target_element;
  Value find_first_result;
  Value find_last_result;
  Value offset_first_result;
  Value offset_last_result;
  Value find_all_result;
  Value offsets_result;
  std::set<LanguageFeature> required_features;
};

static std::vector<ArrayFindFunctionsTestCase>
GetArrayFindFunctionsTestCases() {
  Value null_int64 = NullInt64();

  static const ArrayType* kInt64ArrayType =
      MakeArrayType(types::Int64Type(), type_factory());
  const Value null_int64_array = Null(kInt64ArrayType);
  const Value empty_indices = Value::EmptyArray(kInt64ArrayType);
  const Value indices_1 = Value::Array(kInt64ArrayType, {Value::Int64(1)});
  const Value indices_12 =
      Value::Array(kInt64ArrayType, {Value::Int64(1), Value::Int64(2)});
  const Value indices_13 =
      Value::Array(kInt64ArrayType, {Value::Int64(1), Value::Int64(3)});
  const Value indices_23 =
      Value::Array(kInt64ArrayType, {Value::Int64(2), Value::Int64(3)});
  const Value indices_0 = Value::Array(kInt64ArrayType, {Value::Int64(0)});
  const Value indices_01 =
      Value::Array(kInt64ArrayType, {Value::Int64(0), Value::Int64(1)});

  std::vector<ArrayFindFunctionsTestCase> test_cases;
  std::vector<TypeFeaturePair> pairs = GetOrderableTypesWithFeaturesAndValues();
  ABSL_DCHECK_GT(pairs.size(), 0);
  for (const TypeFeaturePair& v : pairs) {
    const ArrayType* array_type = MakeArrayType(v.type, type_factory());
    Value null_element = Null(v.type);

    // Set up array values with different example input combinations, which are
    // used as input of the test cases.
    // Note that, example_input_2 and example_input_3 are equal.
    Value null_array = Null(array_type);
    Value empty_array = Value::EmptyArray(array_type);
    Value input1 = values::Array(array_type, {v.example_input_1});
    Value input2 = values::Array(array_type, {v.example_input_2});
    Value input3 = values::Array(array_type, {v.example_input_3});
    Value input12 =
        values::Array(array_type, {v.example_input_1, v.example_input_2});
    Value input23 =
        values::Array(array_type, {v.example_input_2, v.example_input_3});
    Value input123 = values::Array(
        array_type, {v.example_input_1, v.example_input_2, v.example_input_3});

    Value input_nulls = values::Array(array_type, {null_element, null_element});
    Value input_1_null =
        values::Array(array_type, {v.example_input_1, null_element});
    Value input_2_null =
        values::Array(array_type, {v.example_input_2, null_element});
    Value input_null_3 =
        values::Array(array_type, {null_element, v.example_input_3});
    Value input_12_null = values::Array(
        array_type, {v.example_input_1, v.example_input_2, null_element});
    Value input_23_null = values::Array(
        array_type, {v.example_input_2, v.example_input_3, null_element});
    Value input_null_123 =
        values::Array(array_type, {null_element, v.example_input_1,
                                   v.example_input_2, v.example_input_3});
    Value input_12_null_3 =
        values::Array(array_type, {v.example_input_1, v.example_input_2,
                                   null_element, v.example_input_3});

    // 1. Empty array argument.
    test_cases.push_back({.input_array = empty_array,
                          .target_element = v.example_input_1,
                          .find_first_result = null_element,
                          .find_last_result = null_element,
                          .offset_first_result = null_int64,
                          .offset_last_result = null_int64,
                          .find_all_result = empty_array,
                          .offsets_result = empty_indices,
                          .required_features = v.required_features});

    test_cases.push_back({.input_array = empty_array,
                          .target_element = null_element,
                          .find_first_result = null_element,
                          .find_last_result = null_element,
                          .offset_first_result = null_int64,
                          .offset_last_result = null_int64,
                          .find_all_result = null_array,
                          .offsets_result = null_int64_array,
                          .required_features = v.required_features});

    // 2. NULL array argument.
    test_cases.push_back({.input_array = null_array,
                          .target_element = null_element,
                          .find_first_result = null_element,
                          .find_last_result = null_element,
                          .offset_first_result = null_int64,
                          .offset_last_result = null_int64,
                          .find_all_result = null_array,
                          .offsets_result = null_int64_array,
                          .required_features = v.required_features});

    test_cases.push_back({.input_array = null_array,
                          .target_element = v.example_input_1,
                          .find_first_result = null_element,
                          .find_last_result = null_element,
                          .offset_first_result = null_int64,
                          .offset_last_result = null_int64,
                          .find_all_result = null_array,
                          .offsets_result = null_int64_array,
                          .required_features = v.required_features});

    // 3. Array argument with all NULL elements.
    test_cases.push_back({.input_array = input_nulls,
                          .target_element = null_element,
                          .find_first_result = null_element,
                          .find_last_result = null_element,
                          .offset_first_result = null_int64,
                          .offset_last_result = null_int64,
                          .find_all_result = null_array,
                          .offsets_result = null_int64_array,
                          .required_features = v.required_features});

    test_cases.push_back({.input_array = input_nulls,
                          .target_element = v.example_input_1,
                          .find_first_result = null_element,
                          .find_last_result = null_element,
                          .offset_first_result = null_int64,
                          .offset_last_result = null_int64,
                          .find_all_result = empty_array,
                          .offsets_result = empty_indices,
                          .required_features = v.required_features});

    // 4. Find unique element from array.
    test_cases.push_back({.input_array = input2,
                          .target_element = v.example_input_2,
                          .find_first_result = v.example_input_2,
                          .find_last_result = v.example_input_2,
                          .offset_first_result = Value::Int64(0),
                          .offset_last_result = Value::Int64(0),
                          .find_all_result = input2,
                          .offsets_result = indices_0,
                          .required_features = v.required_features});

    test_cases.push_back({.input_array = input_2_null,
                          .target_element = v.example_input_2,
                          .find_first_result = v.example_input_2,
                          .find_last_result = v.example_input_2,
                          .offset_first_result = Value::Int64(0),
                          .offset_last_result = Value::Int64(0),
                          .find_all_result = input2,
                          .offsets_result = indices_0,
                          .required_features = v.required_features});

    test_cases.push_back({.input_array = input12,
                          .target_element = v.example_input_2,
                          .find_first_result = v.example_input_2,
                          .find_last_result = v.example_input_2,
                          .offset_first_result = Value::Int64(1),
                          .offset_last_result = Value::Int64(1),
                          .find_all_result = input2,
                          .offsets_result = indices_1,
                          .required_features = v.required_features});

    test_cases.push_back({.input_array = input_12_null,
                          .target_element = v.example_input_2,
                          .find_first_result = v.example_input_2,
                          .find_last_result = v.example_input_2,
                          .offset_first_result = Value::Int64(1),
                          .offset_last_result = Value::Int64(1),
                          .find_all_result = input2,
                          .offsets_result = indices_1,
                          .required_features = v.required_features});

    test_cases.push_back({.input_array = input123,
                          .target_element = v.example_input_1,
                          .find_first_result = v.example_input_1,
                          .find_last_result = v.example_input_1,
                          .offset_first_result = Value::Int64(0),
                          .offset_last_result = Value::Int64(0),
                          .find_all_result = input1,
                          .offsets_result = indices_0,
                          .required_features = v.required_features});

    // 5. Find element with ties from array.
    // Note that, example_input_2 and example_input_3 have equal values.
    test_cases.push_back({.input_array = input23,
                          .target_element = v.example_input_2,
                          .find_first_result = v.example_input_2,
                          .find_last_result = v.example_input_3,
                          .offset_first_result = Value::Int64(0),
                          .offset_last_result = Value::Int64(1),
                          .find_all_result = input23,
                          .offsets_result = indices_01,
                          .required_features = v.required_features});

    test_cases.push_back({.input_array = input_23_null,
                          .target_element = v.example_input_2,
                          .find_first_result = v.example_input_2,
                          .find_last_result = v.example_input_3,
                          .offset_first_result = Value::Int64(0),
                          .offset_last_result = Value::Int64(1),
                          .find_all_result = input23,
                          .offsets_result = indices_01,
                          .required_features = v.required_features});

    test_cases.push_back({.input_array = input123,
                          .target_element = v.example_input_2,
                          .find_first_result = v.example_input_2,
                          .find_last_result = v.example_input_3,
                          .offset_first_result = Value::Int64(1),
                          .offset_last_result = Value::Int64(2),
                          .find_all_result = input23,
                          .offsets_result = indices_12,
                          .required_features = v.required_features});

    test_cases.push_back({.input_array = input_null_123,
                          .target_element = v.example_input_2,
                          .find_first_result = v.example_input_2,
                          .find_last_result = v.example_input_3,
                          .offset_first_result = Value::Int64(2),
                          .offset_last_result = Value::Int64(3),
                          .find_all_result = input23,
                          .offsets_result = indices_23,
                          .required_features = v.required_features});

    test_cases.push_back({.input_array = input_12_null_3,
                          .target_element = v.example_input_2,
                          .find_first_result = v.example_input_2,
                          .find_last_result = v.example_input_3,
                          .offset_first_result = Value::Int64(1),
                          .offset_last_result = Value::Int64(3),
                          .find_all_result = input23,
                          .offsets_result = indices_13,
                          .required_features = v.required_features});
  }
  return test_cases;
}

static std::vector<QueryParamsWithResult> GetArrayFindFunctionsTestCases(
    bool is_safe, bool is_find_offset, bool is_find_multiple) {
  static const EnumType* kArrayFindModeType = types::ArrayFindModeEnumType();
  const Value array_find_mode_first = Value::Enum(kArrayFindModeType, "FIRST");
  const Value array_find_mode_last = Value::Enum(kArrayFindModeType, "LAST");
  const Value array_find_mode_null = Value::Null(kArrayFindModeType);
  Value null_int64 = NullInt64();

  std::vector<QueryParamsWithResult> test_cases;
  for (const ArrayFindFunctionsTestCase& v : GetArrayFindFunctionsTestCases()) {
    const Value null_element = Null(v.target_element.type());
    const Value null_array = Null(v.input_array.type());

    size_t existing_num_tests = test_cases.size();
    if (is_find_offset && !is_find_multiple) {  // ARRAY_OFFSET
      test_cases.push_back(QueryParamsWithResult(
          {v.input_array, v.target_element, array_find_mode_first},
          v.offset_first_result));
      test_cases.push_back(QueryParamsWithResult(
          {v.input_array, v.target_element, array_find_mode_last},
          v.offset_last_result));
      test_cases.push_back(QueryParamsWithResult(
          {v.input_array, v.target_element, array_find_mode_null}, null_int64));
    } else if (!is_find_offset && !is_find_multiple) {  // ARRAY_FIND
      test_cases.push_back(QueryParamsWithResult(
          {v.input_array, v.target_element, array_find_mode_first},
          v.find_first_result));
      test_cases.push_back(QueryParamsWithResult(
          {v.input_array, v.target_element, array_find_mode_last},
          v.find_last_result));
      test_cases.push_back(QueryParamsWithResult(
          {v.input_array, v.target_element, array_find_mode_null},
          null_element));
    } else if (is_find_offset && is_find_multiple) {  // ARRAY_OFFSETS
      test_cases.push_back(QueryParamsWithResult(
          {v.input_array, v.target_element}, v.offsets_result));
    } else {  // ARRAY_FIND_ALL
      test_cases.push_back(QueryParamsWithResult(
          {v.input_array, v.target_element}, v.find_all_result));
    }

    // ARRAY_FIND always takes care of timestamp precision,
    // FEATUE_TIMESTAMP_NANOS is not needed.
    // TODO: Remove this once ARRAY_FIND reference implementation
    // sanitizes the input for nanosecond precision.
    std::set<LanguageFeature> required_features = v.required_features;
    required_features.erase(FEATURE_TIMESTAMP_NANOS);

    for (size_t i = existing_num_tests; i < test_cases.size(); ++i) {
      if (is_safe) {
        test_cases[i].AddRequiredFeature(FEATURE_SAFE_FUNCTION_CALL);
        test_cases[i].AddRequiredFeature(
            FEATURE_SAFE_FUNCTION_CALL_WITH_LAMBDA_ARGS);
      }
      test_cases[i]
          .AddRequiredFeatures(required_features)
          .AddRequiredFeature(FEATURE_ARRAY_FIND_FUNCTIONS);
    }
  }
  return test_cases;
}

static std::vector<QueryParamsWithResult>
GetAndAddWrappedArrayFirstLastFunctionTestResult(bool is_safe, bool is_first) {
  std::vector<ArrayFirstLastTestCase> test_cases = GetArrayFirstLastTestCases();
  std::vector<QueryParamsWithResult> result;
  result.reserve(test_cases.size());

  for (const ArrayFirstLastTestCase& test : test_cases) {
    AddWrappedSafeArrayFunctionResult(
        {test.input}, is_first ? test.output_first : test.output_last,
        test.status_code, test.language_features, is_safe, &result);
  }
  return result;
}

std::vector<QueryParamsWithResult> GetFunctionTestsArrayFirst(bool is_safe) {
  return GetAndAddWrappedArrayFirstLastFunctionTestResult(is_safe,
                                                          /*is_first=*/true);
}

std::vector<QueryParamsWithResult> GetFunctionTestsArrayLast(bool is_safe) {
  return GetAndAddWrappedArrayFirstLastFunctionTestResult(is_safe,
                                                          /*is_first=*/false);
}

std::vector<QueryParamsWithResult> GetFunctionTestsArraySlice(bool is_safe) {
  return GetArraySliceTestCases(is_safe);
}

std::vector<QueryParamsWithResult> GetFunctionTestsArrayMin(bool is_safe) {
  return GetArrayMinMaxTestCases(is_safe, /*is_min=*/true);
}

std::vector<QueryParamsWithResult> GetFunctionTestsArrayMax(bool is_safe) {
  return GetArrayMinMaxTestCases(is_safe, /*is_min=*/false);
}

std::vector<QueryParamsWithResult> GetFunctionTestsArraySum(bool is_safe) {
  return GetArraySumTestCases(is_safe);
}

std::vector<QueryParamsWithResult> GetFunctionTestsArrayAvg(bool is_safe) {
  return GetArrayAvgTestCases(is_safe);
}

std::vector<QueryParamsWithResult> GetFunctionTestsArrayOffset(bool is_safe) {
  return GetArrayFindFunctionsTestCases(is_safe, /*is_find_offset=*/true,
                                        /*is_find_multiple=*/false);
}

std::vector<QueryParamsWithResult> GetFunctionTestsArrayFind(bool is_safe) {
  return GetArrayFindFunctionsTestCases(is_safe, /*is_find_offset=*/false,
                                        /*is_find_multiple=*/false);
}

std::vector<QueryParamsWithResult> GetFunctionTestsArrayOffsets(bool is_safe) {
  return GetArrayFindFunctionsTestCases(is_safe, /*is_find_offset=*/true,
                                        /*is_find_multiple=*/true);
}

std::vector<QueryParamsWithResult> GetFunctionTestsArrayFindAll(bool is_safe) {
  return GetArrayFindFunctionsTestCases(is_safe, /*is_find_offset=*/false,
                                        /*is_find_multiple=*/true);
}

std::vector<QueryParamsWithResult> GetFunctionTestsGreatest() {
  std::vector<QueryParamsWithResult> result;
  for (const ComparisonTest& test : GetComparisonTests(
           /*include_struct_comparisons=*/false,
           /*include_array_comparisons=*/true)) {
    // TODO: This should be 'Equivalent()', not 'Equals()'.  Need
    // to add tests that illustrate the difference.
    if (!test.left.type()->Equals(test.right.type())) {
      continue;  // Inputs to Greatest must all be of the same type.
    }
    Value out;
    switch (test.result) {
      case NULL_VALUE:
        if (test.left.is_null() || test.right.is_null()) {
          out = Value::Null(test.left.type());
        } else {
          // (null vs 1) compares as NULL, but [null] vs [1] orders LESS
          // Since it's NULL_VALUE and neither is the null value, we know
          // it's a case of values raised to singleton arrays
          const Value* arr_with_first_null =
              findArrayWithFirstNull(&test.left, &test.right);
          out = (&test.left == arr_with_first_null ? test.right : test.left);
        }
        break;
      case LESS:
      case EQUAL:
        out = test.right; break;
      case UNORDERED_BUT_ARRAY_ORDERS_LESS:
        if (!test.left.type()->IsArray()) {
          out = test.GetNaN();
        } else {
          out = test.right;
        }
        break;
      case ARRAY_UNEQUAL_ORDERS_LESS:
        out = test.right;
        break;
    }
    // Binary
    AddTestWithPossiblyWrappedResultWithRequiredFeatures(
        {test.left, test.right}, out, test.required_features,
        /*array_language_features=*/
        {FEATURE_ARRAY_ORDERING, FEATURE_ARRAY_GREATEST_LEAST}, &result);
    // Reverse binary
    AddTestWithPossiblyWrappedResultWithRequiredFeatures(
        {test.right, test.left}, out, test.required_features,
        /*array_language_features=*/
        {FEATURE_ARRAY_ORDERING, FEATURE_ARRAY_GREATEST_LEAST}, &result);
    // Ternary
    AddTestWithPossiblyWrappedResultWithRequiredFeatures(
        {test.right, test.left, test.right}, out, test.required_features,
        /*array_language_features=*/
        {FEATURE_ARRAY_ORDERING, FEATURE_ARRAY_GREATEST_LEAST}, &result);
    // Reverse ternary
    AddTestWithPossiblyWrappedResultWithRequiredFeatures(
        {test.left, test.right, test.left}, out, test.required_features,
        /*array_language_features=*/
        {FEATURE_ARRAY_ORDERING, FEATURE_ARRAY_GREATEST_LEAST}, &result);
  }
  return result;
}

std::vector<QueryParamsWithResult> GetFunctionTestsLeast() {
  std::vector<QueryParamsWithResult> result;
  for (const ComparisonTest& test : GetComparisonTests(
           /*include_struct_comparisons=*/false,
           /*include_array_comparisons=*/true)) {
    // TODO: This should be 'Equivalent()', not 'Equals()'.  Need
    // to add tests that illustrate the difference.
    if (!test.left.type()->Equals(test.right.type())) {
      continue;  // Inputs to Least must all be of the same type.
    }
    Value out;
    switch (test.result) {
      case NULL_VALUE:
        if (test.left.is_null() || test.right.is_null()) {
          out = Value::Null(test.left.type());
        } else {
          // (null vs 1) compares as NULL, but [null] vs [1] orders LESS
          // Since it's NULL_VALUE and neither is the null value, we know
          // it's a case of values raised to singleton arrays
          const Value* arr_with_first_null =
              findArrayWithFirstNull(&test.left, &test.right);
          out = (&test.left == arr_with_first_null ? test.left : test.right);
        }
        break;
      case LESS:
      case EQUAL:
        out = test.left; break;
      case UNORDERED_BUT_ARRAY_ORDERS_LESS:
        if (!test.left.type()->IsArray()) {
          out = test.GetNaN();
        } else {
          out = test.left;
        }
        break;
      case ARRAY_UNEQUAL_ORDERS_LESS:
        out = test.left;
        break;
    }
    AddTestWithPossiblyWrappedResultWithRequiredFeatures(
        {test.left, test.right}, out, test.required_features,
        /*array_language_features=*/
        {FEATURE_ARRAY_ORDERING, FEATURE_ARRAY_GREATEST_LEAST}, &result);
    // Reverse binary
    AddTestWithPossiblyWrappedResultWithRequiredFeatures(
        {test.right, test.left}, out, test.required_features,
        /*array_language_features=*/
        {FEATURE_ARRAY_ORDERING, FEATURE_ARRAY_GREATEST_LEAST}, &result);
    // Ternary
    AddTestWithPossiblyWrappedResultWithRequiredFeatures(
        {test.right, test.left, test.right}, out, test.required_features,
        /*array_language_features=*/
        {FEATURE_ARRAY_ORDERING, FEATURE_ARRAY_GREATEST_LEAST}, &result);
    // Reverse ternary
    AddTestWithPossiblyWrappedResultWithRequiredFeatures(
        {test.left, test.right, test.left}, out, test.required_features,
        /*array_language_features=*/
        {FEATURE_ARRAY_ORDERING, FEATURE_ARRAY_GREATEST_LEAST}, &result);
  }
  return result;
}

// FromType is in {float, double}. ToType is in {int32, uint32, int64, uint64}.
template <typename FromType, typename ToType>
static std::vector<QueryParamsWithResult> TestCastPositiveRounding() {
  return {
    {{Value::Make<FromType>(1.1)}, Value::Make<ToType>(1)},
    {{Value::Make<FromType>(1.5)}, Value::Make<ToType>(2)},
    {{Value::Make<FromType>(1.9)}, Value::Make<ToType>(2)},
    {{Value::Make<FromType>(2.5)}, Value::Make<ToType>(3)},
  };
}

// FromType = {float, double}. ToType = {int32, int64}.
template <typename FromType, typename ToType>
static std::vector<QueryParamsWithResult> TestCastNegativeRounding() {
  return {
    {{Value::Make<FromType>(-1.1)}, Value::Make<ToType>(-1)},
    {{Value::Make<FromType>(-1.5)}, Value::Make<ToType>(-2)},
    {{Value::Make<FromType>(-1.9)}, Value::Make<ToType>(-2)},
    {{Value::Make<FromType>(-2.5)}, Value::Make<ToType>(-3)},
  };
}

// FromType is in {float, double}. ToType is in {int32, uint32, int64, uint64,
// bool}.
template <typename FromType, typename ToType>
static std::vector<QueryParamsWithResult> TestCastInfinityError() {
  return {
      {{Value::Make<FromType>(std::numeric_limits<FromType>::quiet_NaN())},
       Value::MakeNull<ToType>(),
       OUT_OF_RANGE},
      {{Value::Make<FromType>(std::numeric_limits<FromType>::infinity())},
       Value::MakeNull<ToType>(),
       OUT_OF_RANGE},
      {{Value::Make<FromType>(-std::numeric_limits<FromType>::infinity())},
       Value::MakeNull<ToType>(),
       OUT_OF_RANGE},
  };
}

// FromType = {int32, int64, uint32, uint64, float, double}.
template <typename FromType>
static std::vector<QueryParamsWithResult> TestCastNumericNull() {
  const Value& from_null = Value::MakeNull<FromType>();
  return {
    {{from_null}, NullInt32()},
    {{from_null}, NullInt64()},
    {{from_null}, NullUint32()},
    {{from_null}, NullUint64()},
    {{from_null}, NullFloat()},
    {{from_null}, NullDouble()},
  };
}

std::vector<QueryParamsWithResult> GetFunctionTestsBitwiseNot() {
  return {
    {{NullInt32()}, NullInt32()},
    {{NullInt64()}, NullInt64()},
    {{NullUint32()}, NullUint32()},
    {{NullUint64()}, NullUint64()},
    {{NullBytes()}, NullBytes()},

    {{Int32(-1)}, Int32(0) },
    {{Int32(0)}, Int32(-1) },
    {{Int32(-2)}, Int32(1) },
    {{Int32(1)}, Int32(-2) },
    {{Int32(0xAAAAAAAA)}, Int32(0x55555555) },
    {{Int32(0x55555555)}, Int32(0xAAAAAAAA) },

    {{Int64(-1)}, Int64(0) },
    {{Int64(0)}, Int64(-1) },
    {{Int64(-2)}, Int64(1) },
    {{Int64(1)}, Int64(-2) },
    {{Int64(0xAAAAAAAAAAAAAAAA)}, Int64(0x5555555555555555) },
    {{Int64(0x5555555555555555)}, Int64(0xAAAAAAAAAAAAAAAA) },

    {{Uint32(0xFFFFFFFF)}, Uint32(0) },
    {{Uint32(0)}, Uint32(0xFFFFFFFF) },
    {{Uint32(0xAAAAAAAA)}, Uint32(0x55555555) },
    {{Uint32(0x55555555)}, Uint32(0xAAAAAAAA) },

    {{Uint64(0xFFFFFFFFFFFFFFFF)}, Uint64(0) },
    {{Uint64(0)}, Uint64(0xFFFFFFFFFFFFFFFF) },
    {{Uint64(0xAAAAAAAAAAAAAAAA)}, Uint64(0x5555555555555555) },
    {{Uint64(0x5555555555555555)}, Uint64(0xAAAAAAAAAAAAAAAA) },

    {{Bytes("")}, Bytes("") },
    {{Bytes("\0")}, Bytes("\xFF") },
    {{Bytes("\xFF")}, Bytes("\0") },
    {{Bytes("\x01\x23\x45\x67\x89\xAB\xCD\xEF\x55\xAA")},
        Bytes("\xFE\xDC\xBA\x98\x76\x54\x32\x10\xAA\x55") },
    {{Bytes("\xFE\xDC\xBA\x98\x76\x54\x32\x10\xAA\x55")},
        Bytes("\x01\x23\x45\x67\x89\xAB\xCD\xEF\x55\xAA") },
  };
}

std::vector<QueryParamsWithResult> GetFunctionTestsBitwiseOr(
    bool with_mode_tests) {
  std::vector<QueryParamsWithResult> tests = {
      {{NullInt32(), Int32(0)}, NullInt32()},
      {{Int32(1), NullInt32()}, NullInt32()},
      {{NullInt64(), Int64(2)}, NullInt64()},
      {{Int64(3), NullInt64()}, NullInt64()},
      {{NullUint32(), Uint32(4)}, NullUint32()},
      {{Uint32(5), NullUint32()}, NullUint32()},
      {{NullUint64(), Uint64(6)}, NullUint64()},
      {{Uint64(7), NullUint64()}, NullUint64()},
      {{NullBytes(), Bytes("foo")}, NullBytes()},
      {{Bytes("foo"), NullBytes()}, NullBytes()},

      {{Int32(0), Int32(0)}, Int32(0)},
      {{Int32(-1), Int32(0)}, Int32(-1)},
      {{Int32(0xAAAAAAAA), Int32(0x55555555)}, Int32(-1)},
      {{Int32(0), Int32(1)}, Int32(1)},
      {{Int32(2), Int32(1)}, Int32(3)},
      {{Int32(0xFF), Int32(1)}, Int32(0xFF)},

      {{Int64(0), Int64(0)}, Int64(0)},
      {{Int64(-1), Int64(0)}, Int64(-1)},
      {{Int64(0xAAAAAAAAAAAAAAAA), Int64(0x5555555555555555)}, Int64(-1)},
      {{Int64(0), Int64(1)}, Int64(1)},
      {{Int64(2), Int64(1)}, Int64(3)},
      {{Int64(0xFF), Int64(1)}, Int64(0xFF)},

      {{Uint32(0), Uint32(0)}, Uint32(0)},
      {{Uint32(0xFFFFFFFFU), Uint32(0)}, Uint32(0xFFFFFFFFU)},
      {{Uint32(0xAAAAAAAA), Uint32(0x55555555)}, Uint32(0xFFFFFFFFU)},
      {{Uint32(0), Uint32(1)}, Uint32(1)},
      {{Uint32(2), Uint32(1)}, Uint32(3)},
      {{Uint32(0xFF), Uint32(1)}, Uint32(0xFF)},

      {{Uint64(0), Uint64(0)}, Uint64(0)},
      {{Uint64(0xFFFFFFFFFFFFFFFFU), Uint64(0)}, Uint64(0xFFFFFFFFFFFFFFFFU)},
      {{Uint64(0xAAAAAAAAAAAAAAAA), Uint64(0x5555555555555555)},
       Uint64(0xFFFFFFFFFFFFFFFFU)},
      {{Uint64(0), Uint64(1)}, Uint64(1)},
      {{Uint64(2), Uint64(1)}, Uint64(3)},
      {{Uint64(0xFF), Uint64(1)}, Uint64(0xFF)},

      {{Bytes(""), Bytes("")}, Bytes("")},
      {{Bytes("\0"), Bytes("\xFF")}, Bytes("\xFF")},
      {{Bytes("\0\xFF"), Bytes("\xFF\0")}, Bytes("\xFF\xFF")},
      {{Bytes("\xAA\xAA\xAA\xAA\xAA"), Bytes("\x55\x55\x55\x55\x55")},
       Bytes("\xFF\xFF\xFF\xFF\xFF")},
      {{Bytes("\x01\x23\x45\x67\x89\xAB\xCD\xEF\xFE\xDC\xBA"),
        Bytes("\x12\x34\x56\x78\x9A\xBC\xDE\xF0\x0F\xED\xCB")},
       Bytes("\x13\x37\x57\x7F\x9B\xBF\xDF\xFF\xFF\xFD\xFB")},
      {{Bytes(""), Bytes("\xFF")}, NullBytes(), OUT_OF_RANGE},
  };
  if (with_mode_tests) {
    const Value strict_mode = Value::Enum(types::BitwiseAggModeEnumType(),
                                          functions::BitwiseAggEnums::STRICT);
    const Value pad_mode = Value::Enum(types::BitwiseAggModeEnumType(),
                                       functions::BitwiseAggEnums::PAD);
    std::vector<QueryParamsWithResult> tests_with_mode = {
        {{Bytes("\x01"), Bytes("\x01\x02"), strict_mode},
         NullBytes(),
         OUT_OF_RANGE},
        {{Bytes("\x01"), Bytes("\x01\x02"), pad_mode}, Bytes("\x01\x02")},
        {{Bytes("\x01\x02"), Bytes("\x01"), strict_mode},
         NullBytes(),
         OUT_OF_RANGE},
        {{Bytes("\x01\x02"), Bytes("\x01"), pad_mode}, Bytes("\x01\x02")}};
    tests.insert(tests.end(), tests_with_mode.begin(), tests_with_mode.end());
  };
  return tests;
}

std::vector<QueryParamsWithResult> GetFunctionTestsBitwiseXor(
    bool with_mode_tests) {
  std::vector<QueryParamsWithResult> tests = {
      {{NullInt32(), Int32(0)}, NullInt32()},
      {{Int32(1), NullInt32()}, NullInt32()},
      {{NullInt64(), Int64(2)}, NullInt64()},
      {{Int64(3), NullInt64()}, NullInt64()},
      {{NullUint32(), Uint32(4)}, NullUint32()},
      {{Uint32(5), NullUint32()}, NullUint32()},
      {{NullUint64(), Uint64(6)}, NullUint64()},
      {{Uint64(7), NullUint64()}, NullUint64()},
      {{NullBytes(), Bytes("foo")}, NullBytes()},
      {{Bytes("foo"), NullBytes()}, NullBytes()},

      {{Int32(0), Int32(0)}, Int32(0)},
      {{Int32(-1), Int32(0)}, Int32(-1)},
      {{Int32(-1), Int32(-1)}, Int32(0)},
      {{Int32(0xAAAAAAAA), Int32(0x55555555)}, Int32(-1)},
      {{Int32(0xAAAAAAAA), Int32(0xFFFFFFFF)}, Int32(0x55555555)},
      {{Int32(2), Int32(1)}, Int32(3)},

      {{Int64(0), Int64(0)}, Int64(0)},
      {{Int64(-1), Int64(0)}, Int64(-1)},
      {{Int64(-1), Int64(-1)}, Int64(0)},
      {{Int64(0xAAAAAAAAAAAAAAAA), Int64(0x5555555555555555)}, Int64(-1)},
      {{Int64(0xAAAAAAAAAAAAAAAA), Int64(0xFFFFFFFFFFFFFFFF)},
       Int64(0x5555555555555555)},
      {{Int64(2), Int64(1)}, Int64(3)},

      {{Uint32(0), Uint32(0)}, Uint32(0)},
      {{Uint32(0xFFFFFFFFU), Uint32(0)}, Uint32(0xFFFFFFFFU)},
      {{Uint32(0xFFFFFFFFU), Uint32(0xFFFFFFFFU)}, Uint32(0)},
      {{Uint32(0xAAAAAAAAU), Uint32(0x55555555U)}, Uint32(0xFFFFFFFFU)},
      {{Uint32(0xAAAAAAAAU), Uint32(0xFFFFFFFFU)}, Uint32(0x55555555U)},
      {{Uint32(2), Uint32(1)}, Uint32(3)},

      {{Uint64(0), Uint64(0)}, Uint64(0)},
      {{Uint64(0xFFFFFFFFFFFFFFFFU), Uint64(0)}, Uint64(0xFFFFFFFFFFFFFFFFU)},
      {{Uint64(0xFFFFFFFFFFFFFFFFU), Uint64(0xFFFFFFFFFFFFFFFFU)}, Uint64(0)},
      {{Uint64(0xAAAAAAAAAAAAAAAAU), Uint64(0x5555555555555555U)},
       Uint64(0xFFFFFFFFFFFFFFFFU)},
      {{Uint64(0xAAAAAAAAAAAAAAAAU), Uint64(0xFFFFFFFFFFFFFFFFU)},
       Uint64(0x5555555555555555U)},
      {{Uint64(2), Uint64(1)}, Uint64(3)},

      {{Bytes(""), Bytes("")}, Bytes("")},
      {{Bytes("\0"), Bytes("\xFF")}, Bytes("\xFF")},
      {{Bytes("\0\xFF"), Bytes("\xFF\0")}, Bytes("\xFF\xFF")},
      {{Bytes("\xAA\xAA\xAA\xAA\xAA"), Bytes("\x55\x55\x55\x55\x55")},
       Bytes("\xFF\xFF\xFF\xFF\xFF")},
      {{Bytes("\x01\x23\x45\x67\x89\xAB\xCD\xEF\xFE\xDC\xBA"),
        Bytes("\x12\x34\x56\x78\x9A\xBC\xDE\xF0\x0F\xED\xCB")},
       Bytes("\x13\x17\x13\x1F\x13\x17\x13\x1F\xF1\x31\x71")},
      {{Bytes(""), Bytes("\xFF")}, NullBytes(), OUT_OF_RANGE},
  };
  if (with_mode_tests) {
    const Value strict_mode = Value::Enum(types::BitwiseAggModeEnumType(),
                                          functions::BitwiseAggEnums::STRICT);
    const Value pad_mode = Value::Enum(types::BitwiseAggModeEnumType(),
                                       functions::BitwiseAggEnums::PAD);
    std::vector<QueryParamsWithResult> tests_with_mode = {
        {{Bytes("\x01"), Bytes("\x01\x02"), strict_mode},
         NullBytes(),
         OUT_OF_RANGE},
        {{Bytes("\x01"), Bytes("\x01\x02"), pad_mode}, Bytes("\x00\x02")},
        {{Bytes("\x01\x02"), Bytes("\x01"), strict_mode},
         NullBytes(),
         OUT_OF_RANGE},
        {{Bytes("\x01\x02"), Bytes("\x01"), pad_mode}, Bytes("\x00\x02")},
    };
    tests.insert(tests.end(), tests_with_mode.begin(), tests_with_mode.end());
  }
  return tests;
}

std::vector<QueryParamsWithResult> GetFunctionTestsBitwiseAnd(
    bool with_mode_tests) {
  std::vector<QueryParamsWithResult> tests = {
      {{NullInt32(), Int32(0)}, NullInt32()},
      {{Int32(1), NullInt32()}, NullInt32()},
      {{NullInt64(), Int64(2)}, NullInt64()},
      {{Int64(3), NullInt64()}, NullInt64()},
      {{NullUint32(), Uint32(4)}, NullUint32()},
      {{Uint32(5), NullUint32()}, NullUint32()},
      {{NullUint64(), Uint64(6)}, NullUint64()},
      {{Uint64(7), NullUint64()}, NullUint64()},
      {{NullBytes(), Bytes("foo")}, NullBytes()},
      {{Bytes("foo"), NullBytes()}, NullBytes()},

      {{Int32(0), Int32(0)}, Int32(0)},
      {{Int32(-1), Int32(0)}, Int32(0)},
      {{Int32(0xAAAAAAAA), Int32(0x55555555)}, Int32(0)},
      {{Int32(0xAAAAAAAA), Int32(0xFFFFFFFF)}, Int32(0xAAAAAAAA)},
      {{Int32(0xAAAAAAAA), Int32(0xF0F0F0F0)}, Int32(0xA0A0A0A0)},

      {{Int64(0), Int64(0)}, Int64(0)},
      {{Int64(-1), Int64(0)}, Int64(0)},
      {{Int64(0xAAAAAAAAAAAAAAAA), Int64(0x5555555555555555)}, Int64(0)},
      {{Int64(0xAAAAAAAAAAAAAAAA), Int64(0xFFFFFFFFFFFFFFFF)},
       Int64(0xAAAAAAAAAAAAAAAA)},
      {{Int64(0xAAAAAAAAAAAAAAAA), Int64(0xF0F0F0F0F0F0F0F0)},
       Int64(0xA0A0A0A0A0A0A0A0)},

      {{Uint32(0), Uint32(0)}, Uint32(0)},
      {{Uint32(0xFFFFFFFFU), Uint32(0)}, Uint32(0)},
      {{Uint32(0xAAAAAAAAU), Uint32(0x55555555U)}, Uint32(0)},
      {{Uint32(0xAAAAAAAAU), Uint32(0xFFFFFFFFU)}, Uint32(0xAAAAAAAAU)},
      {{Uint32(0xAAAAAAAAU), Uint32(0xF0F0F0F0U)}, Uint32(0xA0A0A0A0U)},

      {{Uint64(0), Uint64(0)}, Uint64(0)},
      {{Uint64(0xFFFFFFFFFFFFFFFFU), Uint64(0)}, Uint64(0)},
      {{Uint64(0xAAAAAAAAAAAAAAAAU), Uint64(0x5555555555555555U)}, Uint64(0)},
      {{Uint64(0xAAAAAAAAAAAAAAAAU), Uint64(0xFFFFFFFFFFFFFFFFU)},
       Uint64(0xAAAAAAAAAAAAAAAAU)},
      {{Uint64(0xAAAAAAAAAAAAAAAAU), Uint64(0xF0F0F0F0F0F0F0F0U)},
       Uint64(0xA0A0A0A0A0A0A0A0U)},

      {{Bytes(""), Bytes("")}, Bytes("")},
      {{Bytes("\0"), Bytes("\xFF")}, Bytes("\0")},
      {{Bytes("\0\xFF"), Bytes("\xFF\0")}, Bytes("\0\0")},
      {{Bytes("\xAA\xAA\xAA\xAA\xAA"), Bytes("\x55\x55\x55\x55\x55")},
       Bytes("\0\0\0\0\0")},
      {{Bytes("\x01\x23\x45\x67\x89\xAB\xCD\xEF\xFE\xDC\xBA"),
        Bytes("\x12\x34\x56\x78\x9A\xBC\xDE\xF0\x0F\xED\xCB")},
       Bytes("\x00\x20\x44\x60\x88\xA8\xCC\xE0\x0E\xCC\x8A")},
      {{Bytes(""), Bytes("\xFF")}, NullBytes(), OUT_OF_RANGE},
  };
  if (with_mode_tests) {
    const Value strict_mode = Value::Enum(types::BitwiseAggModeEnumType(),
                                          functions::BitwiseAggEnums::STRICT);
    const Value pad_mode = Value::Enum(types::BitwiseAggModeEnumType(),
                                       functions::BitwiseAggEnums::PAD);
    std::vector<QueryParamsWithResult> tests_with_mode = {
        {{Bytes("\x01"), Bytes("\x01\x02"), strict_mode},
         NullBytes(),
         OUT_OF_RANGE},
        {{Bytes("\x01"), Bytes("\x01\x02"), pad_mode}, Bytes("\x01\x00")},
        {{Bytes("\x01\x02"), Bytes("\x01"), strict_mode},
         NullBytes(),
         OUT_OF_RANGE},
        {{Bytes("\x01\x02"), Bytes("\x01"), pad_mode}, Bytes("\x01\x00")},
    };
    tests.insert(tests.end(), tests_with_mode.begin(), tests_with_mode.end());
  }
  return tests;
}

std::vector<QueryParamsWithResult> GetFunctionTestsBitwiseLeftShift() {
  return {
      {{NullInt32(), Int64(0)}, NullInt32()},
      {{NullInt64(), Int64(0)}, NullInt64()},
      {{NullUint32(), Int64(0)}, NullUint32()},
      {{NullUint64(), Int64(0)}, NullUint64()},
      {{NullBytes(), Int64(0)}, NullBytes()},
      {{Int32(0), NullInt64()}, NullInt32()},
      {{Int64(0), NullInt64()}, NullInt64()},
      {{Uint32(0), NullInt64()}, NullUint32()},
      {{Uint64(0), NullInt64()}, NullUint64()},
      {{Bytes("foo"), NullInt64()}, NullBytes()},

      {{Int32(-1), Int64(0)}, Int32(-1)},
      {{Int32(-1), Int64(32)}, Int32(0)},
      {{Int32(-1), Int64(1)}, Int32(-2)},
      {{Int32(0x5555), Int64(16)}, Int32(0x55550000)},
      {{Int32(0x5555), Int64(-1)}, NullInt32(), OUT_OF_RANGE},

      {{Int64(-1), Int64(0)}, Int64(-1)},
      {{Int64(-1), Int64(64)}, Int64(0)},
      {{Int64(-1), Int64(1)}, Int64(-2)},
      {{Int64(0x5555), Int64(48)}, Int64(0x5555000000000000)},
      {{Int64(0x5555), Int64(-1)}, NullInt64(), OUT_OF_RANGE},

      {{Uint32(0xFFFFFFFFU), Int64(0)}, Uint32(0xFFFFFFFFU)},
      {{Uint32(0xFFFFFFFFU), Int64(32)}, Uint32(0)},
      {{Uint32(0xFFFFFFFFU), Int64(1)}, Uint32(0xFFFFFFFEU)},
      {{Uint32(0x5555), Int64(16)}, Uint32(0x55550000U)},
      {{Uint32(0x5555), Int64(-1)}, NullUint32(), OUT_OF_RANGE},

      {{Uint64(0xFFFFFFFFFFFFFFFFU), Int64(0)}, Uint64(0xFFFFFFFFFFFFFFFFU)},
      {{Uint64(0xFFFFFFFFFFFFFFFFU), Int64(64)}, Uint64(0)},
      {{Uint64(0xFFFFFFFFFFFFFFFFU), Int64(1)}, Uint64(0xFFFFFFFFFFFFFFFEU)},
      {{Uint64(0x5555), Int64(48)}, Uint64(0x5555000000000000U)},
      {{Uint64(0x5555), Int64(-1)}, NullUint64(), OUT_OF_RANGE},

      {{Bytes("\xFE\xDC\xBA\x98\x76"), Int64(0)},
       Bytes("\xFE\xDC\xBA\x98\x76")},
      {{Bytes("\xFE\xDC\xBA\x98\x76"), Int64(40)},
       Bytes("\x00\x00\x00\x00\x00")},
      {{Bytes("\xFE\xDC\xBA\x98\x76"), Int64(41)},
       Bytes("\x00\x00\x00\x00\x00")},
      {{Bytes("\xFE\xDC\xBA\x98\x76"), Int64(1)},
       Bytes("\xFD\xB9\x75\x30\xEC")},
      {{Bytes("\xFE\xDC\xBA\x98\x76"), Int64(4)},
       Bytes("\xED\xCB\xA9\x87\x60")},
      {{Bytes("\xFE\xDC\xBA\x98\x76"), Int64(17)},
       Bytes("\x75\x30\xEC\x00\x00")},
      {{Bytes("\xFE\xDC\xBA\x98\x76"), Int64(24)},
       Bytes("\x98\x76\x00\x00\x00")},
      {{Bytes("\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"), Int64(64)},
       Bytes("\xFF\xFF\xFF\xFF\0\0\0\0\0\0\0\0")},
      {{Bytes("\xFE\xDC\xBA\x98\x76"), Int64(-1)}, NullBytes(), OUT_OF_RANGE},
      // Empty input.
      {{Bytes(""), Int64(0)}, Bytes("")},
      {{Bytes(""), Int64(10)}, Bytes("")},
  };
}

std::vector<QueryParamsWithResult> GetFunctionTestsBitwiseRightShift() {
  return {
      {{NullInt32(), Int64(0)}, NullInt32()},
      {{NullInt64(), Int64(2)}, NullInt64()},
      {{NullUint32(), Int64(4)}, NullUint32()},
      {{NullUint64(), Int64(6)}, NullUint64()},
      {{NullBytes(), Int64(0)}, NullBytes()},
      {{Int32(0), NullInt64()}, NullInt32()},
      {{Int64(0), NullInt64()}, NullInt64()},
      {{Uint32(0), NullInt64()}, NullUint32()},
      {{Uint64(0), NullInt64()}, NullUint64()},
      {{Bytes("foo"), NullInt64()}, NullBytes()},

      {{Int32(-1), Int64(0)}, Int32(-1)},
      {{Int32(-1), Int64(32)}, Int32(0)},
      {{Int32(-1), Int64(1)}, Int32(0x7FFFFFFF)},
      {{Int32(0x55550000), Int64(16)}, Int32(0x5555)},
      {{Int32(0x5555), Int64(-1)}, NullInt32(), OUT_OF_RANGE},

      {{Int64(-1), Int64(0)}, Int64(-1)},
      {{Int64(-1), Int64(64)}, Int64(0)},
      {{Int64(-1), Int64(1)}, Int64(0x7FFFFFFFFFFFFFFF)},
      {{Int64(0x5555000000000000), Int64(48)}, Int64(0x5555)},
      {{Int64(0x5555), Int64(-1)}, NullInt64(), OUT_OF_RANGE},

      {{Uint32(0xFFFFFFFFU), Int64(0)}, Uint32(0xFFFFFFFFU)},
      {{Uint32(0xFFFFFFFFU), Int64(32)}, Uint32(0)},
      {{Uint32(0xFFFFFFFFU), Int64(1)}, Uint32(0x7FFFFFFFU)},
      {{Uint32(0x55550000U), Int64(16)}, Uint32(0x5555)},
      {{Uint32(0x5555), Int64(-1)}, NullUint32(), OUT_OF_RANGE},

      {{Uint64(0xFFFFFFFFFFFFFFFFU), Int64(0)}, Uint64(0xFFFFFFFFFFFFFFFFU)},
      {{Uint64(0xFFFFFFFFFFFFFFFFU), Int64(64)}, Uint64(0)},
      {{Uint64(0xFFFFFFFFFFFFFFFFU), Int64(1)}, Uint64(0x7FFFFFFFFFFFFFFFU)},
      {{Uint64(0x5555000000000000U), Int64(48)}, Uint64(0x5555)},
      {{Uint64(0x5555), Int64(-1)}, NullUint64(), OUT_OF_RANGE},

      {{Bytes("\xFE\xDC\xBA\x98\x76"), Int64(0)},
       Bytes("\xFE\xDC\xBA\x98\x76")},
      {{Bytes("\xFE\xDC\xBA\x98\x76"), Int64(40)},
       Bytes("\x00\x00\x00\x00\x00")},
      {{Bytes("\xFE\xDC\xBA\x98\x76"), Int64(41)},
       Bytes("\x00\x00\x00\x00\x00")},
      {{Bytes("\xFE\xDC\xBA\x98\x76"), Int64(1)},
       Bytes("\x7F\x6E\x5D\x4C\x3B")},
      {{Bytes("\xFE\xDC\xBA\x98\x76"), Int64(4)},
       Bytes("\x0F\xED\xCB\xA9\x87")},
      {{Bytes("\xFE\xDC\xBA\x98\x76"), Int64(17)},
       Bytes("\x00\x00\x7F\x6E\x5D")},
      {{Bytes("\xFE\xDC\xBA\x98\x76"), Int64(24)},
       Bytes("\x00\x00\x00\xFE\xDC")},
      {{Bytes("\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"), Int64(64)},
       Bytes("\0\0\0\0\0\0\0\0\xFF\xFF\xFF\xFF")},
      {{Bytes("\xFE\xDC\xBA\x98\x76"), Int64(-1)}, NullBytes(), OUT_OF_RANGE},
      // Empty input.
      {{Bytes(""), Int64(0)}, Bytes("")},
      {{Bytes(""), Int64(10)}, Bytes("")},
  };
}

std::vector<QueryParamsWithResult> GetFunctionTestsBitCount() {
  return {
      {{NullInt32()}, NullInt64()},
      {{NullInt64()}, NullInt64()},
      {{NullUint64()}, NullInt64()},
      {{NullBytes()}, NullInt64()},

      {{Int32(0)}, Int64(0)},
      {{Int32(-1)}, Int64(32)},
      {{Int32(-2)}, Int64(31)},
      {{Int32(0xFFFF)}, Int64(16)},
      {{Int32(0xFFFFFF)}, Int64(24)},
      {{Int32(0xFFFFFFF)}, Int64(28)},
      {{Int32(0x7FFFFFFF)}, Int64(31)},
      {{Int32(0x7F0F0F0F)}, Int64(19)},
      {{Int32(0x88888888)}, Int64(8)},

      {{Uint32(0)}, Int64(0)},
      {{Uint32(0xFFFF)}, Int64(16)},
      {{Uint32(0xFFFFFF)}, Int64(24)},
      {{Uint32(0xFFFFFFF)}, Int64(28)},
      {{Uint32(0x7FFFFFFF)}, Int64(31)},
      {{Uint32(0xFFFFFFFFU)}, Int64(32)},
      {{Uint32(0x7F0F0F0F)}, Int64(19)},
      {{Uint32(0x88888888)}, Int64(8)},

      {{Int64(0)}, Int64(0)},
      {{Int64(-1)}, Int64(64)},
      {{Int64(-2)}, Int64(63)},
      {{Int64(0xFFFF)}, Int64(16)},
      {{Int64(0xFFFFFF)}, Int64(24)},
      {{Int64(0xFFFFFFF)}, Int64(28)},
      {{Int64(0x7FFFFFFF)}, Int64(31)},
      {{Int64(0xFFFFFFFFL)}, Int64(32)},
      {{Int64(0xFFFFFFFFFFL)}, Int64(40)},
      {{Int64(0xFFFFFFFFFFFFFFL)}, Int64(56)},
      {{Int64(0xFFFFFFFFFFFFFFFL)}, Int64(60)},
      {{Int64(0x7FFFFFFFFFFFFFFFL)}, Int64(63)},

      {{Uint64(0)}, Int64(0)},
      {{Uint64(-1)}, Int64(64)},
      {{Uint64(-2)}, Int64(63)},
      {{Uint64(0xFFFF)}, Int64(16)},
      {{Uint64(0xFFFFFF)}, Int64(24)},
      {{Uint64(0xFFFFFFF)}, Int64(28)},
      {{Uint64(0x7FFFFFFF)}, Int64(31)},
      {{Uint64(0xFFFFFFFFUL)}, Int64(32)},
      {{Uint64(0xFFFFFFFFFFUL)}, Int64(40)},
      {{Uint64(0xFFFFFFFFFFFFFFUL)}, Int64(56)},
      {{Uint64(0xFFFFFFFFFFFFFFFUL)}, Int64(60)},
      {{Uint64(0x7FFFFFFFFFFFFFFFUL)}, Int64(63)},
      {{Uint64(0xFFFFFFFFFFFFFFFFUL)}, Int64(64)},

      {{Bytes("")}, Int64(0)},
      {{Bytes("\x00")}, Int64(0)},
      {{Bytes("\xFF")}, Int64(8)},
      {{Bytes("\x78")}, Int64(4)},
      {{Bytes("\x00\x00")}, Int64(0)},
      {{Bytes("\xFF\xFF")}, Int64(16)},
      {{Bytes("\xFF\x00")}, Int64(8)},
      {{Bytes("\x00\xFF")}, Int64(8)},
      {{Bytes("\x12\x34")}, Int64(5)},
      {{Bytes("\x01\x02\x03\x04\x05\x06\x07\x08")}, Int64(13)},
      {{Bytes("\x01\x02\x03\x04\x05\x06\x07\x08\x09")}, Int64(15)},
      {{Bytes("\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a")}, Int64(17)},
      {{Bytes("\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b")}, Int64(20)},
      {{Bytes("\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF")}, Int64(96)},
  };
}

std::vector<QueryParamsWithResult> GetFunctionTestsAtOffset() {
  const Value int64_array =
      Value::Array(Int64ArrayType(), {Int64(10), Int64(11)});
  const Value string_array =
      Value::Array(StringArrayType(), {String("foo"), String("bar")});
  const StructType* struct_type = SimpleStructType();  // a: string, b: int32
  const ArrayType* array_struct_type;
  ZETASQL_CHECK_OK(type_factory()->MakeArrayType(struct_type, &array_struct_type));
  const Value struct0 = Value::Struct(struct_type, {String("foo"), Int32(0)});
  const Value struct1 = Value::Struct(struct_type, {String("bar"), Int32(1)});
  const Value struct_array =
      Value::Array(array_struct_type, {struct0, struct1});
  return {
    {{int64_array, Int64(-1)}, NullInt64(), OUT_OF_RANGE},
    {{int64_array, Int64(0)}, Int64(10)},
    {{int64_array, Int64(1)}, Int64(11)},
    {{int64_array, Int64(2)}, NullInt64(), OUT_OF_RANGE},
    {{string_array, Int64(-1)}, NullString(), OUT_OF_RANGE},
    {{string_array, Int64(0)}, String("foo")},
    {{string_array, Int64(1)}, String("bar")},
    {{string_array, Int64(2)}, NullString(), OUT_OF_RANGE},
    {{struct_array, Int64(-1)}, Null(struct_type), OUT_OF_RANGE},
    {{struct_array, Int64(0)}, struct0},
    {{struct_array, Int64(1)}, struct1},
    {{struct_array, Int64(2)}, Null(struct_type), OUT_OF_RANGE},
  };
}

std::vector<QueryParamsWithResult> GetFunctionTestsSafeAtOffset() {
  // Use the same tests as above, but return null rather than an error when the
  // position is out of bounds.
  std::vector<QueryParamsWithResult> offset_tests = GetFunctionTestsAtOffset();
  for (auto& offset_test : offset_tests) {
    offset_test.MutateResult([](QueryParamsWithResult::Result& mutable_result) {
      mutable_result.status = absl::OkStatus();
    });
  }
  return offset_tests;
}

std::vector<QueryParamsWithResult> GetFunctionTestsIf() {
  std::vector<QueryParamsWithResult> result;
  for (const auto& v : GetRowsOfValues()) {
    result.push_back({{NullBool(), v[0], v[1]}, v[1]});
    result.push_back({{False(), v[0], v[1]}, v[1]});
    result.push_back({{True(), v[0], v[1]}, v[0]});
    result.push_back({{NullBool(), v[1], v[0]}, v[0]});
    result.push_back({{False(), v[1], v[0]}, v[0]});
    result.push_back({{True(), v[1], v[0]}, v[1]});
  }
  WrapNumericTestCases(&result);
  WrapBigNumericTestCases(&result);
  WrapRangeTestCases(&result);
  return result;
}

std::vector<QueryParamsWithResult> GetFunctionTestsIfNull() {
  std::vector<QueryParamsWithResult> result;
  for (const auto& v : GetRowsOfValues()) {
    result.push_back({{Null(v[0].type()), v[0]}, v[0]});
    result.push_back({{v[0], v[1]}, v[0]});
    result.push_back({{v[1], v[0]}, v[1]});
  }
  WrapNumericTestCases(&result);
  WrapBigNumericTestCases(&result);
  WrapRangeTestCases(&result);
  return result;
}

// Builds test cases with FEATURE_ARRAY_EQUALITY option turned on/off.
static QueryParamsWithResult BuildArrayEqualityQueryParamsWithResult(
    absl::Span<const ValueConstructor> arguments,
    const ValueConstructor& result, const Value& null_value) {
  return QueryParamsWithResult(arguments, result)
      .AddRequiredFeature(FEATURE_ARRAY_EQUALITY);
}

std::vector<QueryParamsWithResult> GetFunctionTestsNullIf() {
  std::vector<QueryParamsWithResult> result;
  for (const auto& v : GetRowsOfValues()) {
    const Type* type = v[0].type();
    Value null_value = Null(type);
    if (type->IsProto()) {
      // SQL equality is not defined for protos.
      continue;
    }
    if (type->IsArray()) {
      result.push_back(BuildArrayEqualityQueryParamsWithResult(
          {null_value, v[0]}, null_value, null_value));
      result.push_back(BuildArrayEqualityQueryParamsWithResult(
          {v[0], null_value}, v[0], null_value));
      result.push_back(BuildArrayEqualityQueryParamsWithResult(
          {v[0], v[1]}, v[0], null_value));
      result.push_back(BuildArrayEqualityQueryParamsWithResult(
          {v[1], v[0]}, v[1], null_value));
      result.push_back(BuildArrayEqualityQueryParamsWithResult(
          {v[0], v[0]}, null_value, null_value));
      result.push_back(BuildArrayEqualityQueryParamsWithResult(
          {v[1], v[1]}, null_value, null_value));
      continue;
    }
    result.push_back({{null_value, v[0]}, null_value});
    result.push_back({{v[0], null_value}, v[0]});
    result.push_back({{v[0], v[1]}, v[0]});
    result.push_back({{v[1], v[0]}, v[1]});
    result.push_back({{v[0], v[0]}, null_value});
    result.push_back({{v[1], v[1]}, null_value});
  }
  WrapNumericTestCases(&result);
  WrapBigNumericTestCases(&result);
  WrapRangeTestCases(&result);
  return result;
}

struct NullIfZeroTestCase {
  Value zero_value;
  Value null_value;
  Value nonzero_value;
};

// Each returned row contains 0, NULL, and a nonzero value.
// There is one row for each zeroable type.
static std::vector<NullIfZeroTestCase> GetRowsOfZeroableValues() {
  std::vector<NullIfZeroTestCase> v = {{.zero_value = Int32(0),
                                        .null_value = NullInt32(),
                                        .nonzero_value = Int32(1)},
                                       {.zero_value = Int64(0),
                                        .null_value = NullInt64(),
                                        .nonzero_value = Int64(1)},
                                       {.zero_value = Uint32(0),
                                        .null_value = NullUint32(),
                                        .nonzero_value = Uint32(1)},
                                       {.zero_value = Uint64(0),
                                        .null_value = NullUint64(),
                                        .nonzero_value = Uint64(1)},
                                       {.zero_value = Float(0.0),
                                        .null_value = NullFloat(),
                                        .nonzero_value = Float(1.1)},
                                       {.zero_value = Double(0.0),
                                        .null_value = NullDouble(),
                                        .nonzero_value = Double(1.1)},
                                       {.zero_value = NumericFromDouble(0.0),
                                        .null_value = NullNumeric(),
                                        .nonzero_value = Numeric(-100)},
                                       {.zero_value = BigNumericFromDouble(0.0),
                                        .null_value = NullBigNumeric(),
                                        .nonzero_value = BigNumeric(-100)}};
  return v;
}

std::vector<QueryParamsWithResult> GetFunctionTestsZeroIfNull_NullIfZero(
    bool is_zero_if_null, bool is_safe) {
  std::vector<QueryParamsWithResult> result;
  for (const auto& v : GetRowsOfZeroableValues()) {
    if (is_zero_if_null) {
      result.push_back({{v.zero_value}, v.zero_value});
    } else {
      result.push_back({{v.zero_value}, v.null_value});
    }
    if (is_zero_if_null) {
      result.push_back({{v.null_value}, v.zero_value});
    } else {
      result.push_back({{v.null_value}, v.null_value});
    }
    result.push_back({{v.nonzero_value}, v.nonzero_value});
  }
  WrapNumericTestCases(&result);
  WrapBigNumericTestCases(&result);
  for (auto& test_case : result) {
    if (is_safe) {
      test_case.AddRequiredFeature(FEATURE_SAFE_FUNCTION_CALL);
    }
    test_case.AddRequiredFeature(FEATURE_NULLIFZERO_ZEROIFNULL);
  }
  return result;
}

std::vector<QueryParamsWithResult> GetFunctionTestsCoalesce() {
  std::vector<QueryParamsWithResult> result;
  for (const auto& v : GetRowsOfValues()) {
    Value null_value = Null(v[0].type());
    result.push_back({{null_value}, null_value});
    result.push_back({{v[0], null_value}, v[0]});
    result.push_back({{null_value, v[0]}, v[0]});
    result.push_back({{null_value, v[0], v[1]}, v[0]});
    result.push_back({{null_value, v[1], v[0]}, v[1]});
    result.push_back({{null_value, v[0], null_value, v[1]}, v[0]});
    result.push_back({{null_value, v[1], null_value, v[0]}, v[1]});
    result.push_back({{v[0], v[1]}, v[0]});
    result.push_back({{v[1], v[0]}, v[1]});
  }
  WrapNumericTestCases(&result);
  WrapBigNumericTestCases(&result);
  WrapRangeTestCases(&result);
  return result;
}


std::vector<FunctionTestCall> GetArrayConcatTests() {
  const Value empty_array = Value::EmptyArray(Int32ArrayType());
  const Value null_array = Value::Null(Int32ArrayType());
  const Value array_12 = Value::Array(Int32ArrayType(), {Int32(1), Int32(2)});
  const Value array_23 = Value::Array(Int32ArrayType(), {Int32(2), Int32(3)});
  const Value array_3 = Value::Array(Int32ArrayType(), {Int32(3)});
  const Value array_1223 = Value::Array(
      Int32ArrayType(), {Int32(1), Int32(2), Int32(2), Int32(3)});
  const Value array_2312 = Value::Array(
      Int32ArrayType(), {Int32(2), Int32(3), Int32(1), Int32(2)});
  const Value array_23123 = Value::Array(
      Int32ArrayType(), {Int32(2), Int32(3), Int32(1), Int32(2), Int32(3)});
  const Value array_ab = Value::Array(StringArrayType(),
                                      {String("a"), String("b")});
  const Value array_c = Value::Array(StringArrayType(), {String("c")});
  const Value array_abc = Value::Array(StringArrayType(),
                                      {String("a"), String("b"), String("c")});
  return {
      // array_concat(array...) -> array
      {"array_concat", {empty_array}, empty_array},
      {"array_concat", {empty_array, empty_array}, empty_array},
      {"array_concat", {empty_array, null_array}, null_array},
      {"array_concat", {null_array}, null_array},
      {"array_concat", {array_12}, array_12},
      {"array_concat", {empty_array, array_12}, array_12},
      {"array_concat", {array_12, empty_array}, array_12},
      {"array_concat", {null_array, array_12}, null_array},
      {"array_concat", {array_12, array_23}, array_1223},
      {"array_concat", {array_23, array_12}, array_2312},
      {"array_concat", {array_23, array_12, array_3}, array_23123},
      {"array_concat", {array_ab}, array_ab},
      {"array_concat", {array_ab, array_c}, array_abc},
  };
}

std::vector<FunctionTestCall> GetFunctionTestsArray() {
  const Value array_ab = Value::Array(StringArrayType(),
                                      {String("a"), String("b")});
  const Value array_c = Value::Array(StringArrayType(), {String("c")});
  const Value array_abc = Value::Array(StringArrayType(),
                                      {String("a"), String("b"), String("c")});
  const Value array_ab_nulls =
      Value::Array(StringArrayType(),
                   {NullString(), String("a"), NullString(), String("b")});
  const Value array_empty_string = Value::Array(StringArrayType(),
                                                {String("")});
  const Value array_empty_string_2 = Value::Array(StringArrayType(),
                                                  {String(""), String("")});
  const Value array_empty_string_3 = Value::Array(StringArrayType(),
                                                  {String("a"), String("")});
  const Value array_empty_string_4 = Value::Array(StringArrayType(),
                                                  {String(""), String("b")});
  const Value array_empty_string_5 =
      Value::Array(StringArrayType(), {String(""), String("b"), String("")});
  const Value array_empty_string_6 =
      Value::Array(StringArrayType(),
                   {String("a"), String(""), String(""), String("b")});

  const Value array_ab_bytes = Value::Array(
      BytesArrayType(), {Bytes("a"), Bytes("b")});
  const Value array_c_bytes = Value::Array(BytesArrayType(), {Bytes("c")});
  const Value array_abc_bytes = Value::Array(
      BytesArrayType(), {Bytes("a"), Bytes("b"), Bytes("c")});
  const Value array_ab_nulls_bytes =
      Value::Array(BytesArrayType(),
                   {NullBytes(), Bytes("a"), NullBytes(), Bytes("b")});
  const Value array_empty_bytes = Value::Array(BytesArrayType(), {Bytes("")});

  const StructType* struct_type = SimpleStructType();  // a: string, b: int32
  const Value struct0 = Value::Struct(struct_type, {String("foo"), Int32(0)});
  const Value struct1 = Value::Struct(struct_type, {String("bar"), Int32(1)});
  const Value struct2 = Value::Struct(struct_type, {String("baz"), Int32(2)});
  const ArrayType* array_struct_type = MakeArrayType(struct_type);

  std::vector<FunctionTestCall> results = {
      // array_to_string -> string
      {"array_to_string", {Null(StringArrayType()), String(",")}, NullString()},
      {"array_to_string", {array_ab, NullString()}, NullString()},
      {"array_to_string", {array_ab, String(","), NullString()}, NullString()},
      {"array_to_string",
       {EmptyArray(StringArrayType()), String(",")},
       String("")},
      {"array_to_string", {array_empty_string, String(",")}, String("")},
      {"array_to_string", {array_empty_string_2, String(",")}, String(",")},
      {"array_to_string", {array_empty_string_3, String(",")}, String("a,")},
      {"array_to_string", {array_empty_string_4, String(",")}, String(",b")},
      {"array_to_string", {array_empty_string_5, String(",")}, String(",b,")},
      {"array_to_string", {array_empty_string_6, String(",")}, String("a,,,b")},

      {"array_to_string", {array_c, String(",")}, String("c")},
      {"array_to_string", {array_abc, String(",")}, String("a,b,c")},
      {"array_to_string", {array_abc, String("+!+")}, String("a+!+b+!+c")},
      {"array_to_string", {array_abc, String("")}, String("abc")},
      {"array_to_string", {array_ab_nulls, String(",")}, String("a,b")},
      {"array_to_string",
       {array_ab_nulls, String(","), String("")},
       String(",a,,b")},
      {"array_to_string",
       {array_ab_nulls, String(","), String("*")},
       String("*,a,*,b")},
      {"array_to_string",
       {array_ab_nulls, String(","), String("<null>")},
       String("<null>,a,<null>,b")},

      // array_to_string -> bytes
      {"array_to_string", {Null(BytesArrayType()), Bytes(",")}, NullBytes()},
      {"array_to_string", {array_ab_bytes, NullBytes()}, NullBytes()},
      {"array_to_string",
       {array_ab_bytes, Bytes(","), NullBytes()},
       NullBytes()},
      {"array_to_string",
       {EmptyArray(BytesArrayType()), Bytes(",")},
       Bytes("")},
      {"array_to_string", {array_empty_bytes, Bytes(",")}, Bytes("")},
      {"array_to_string", {array_c_bytes, Bytes(",")}, Bytes("c")},
      {"array_to_string", {array_abc_bytes, Bytes(",")}, Bytes("a,b,c")},
      {"array_to_string", {array_abc_bytes, Bytes("\1")}, Bytes("a\1b\1c")},
      {"array_to_string", {array_abc_bytes, Bytes("")}, Bytes("abc")},
      {"array_to_string", {array_ab_nulls_bytes, Bytes(",")}, Bytes("a,b")},
      {"array_to_string",
       {array_ab_nulls_bytes, Bytes(","), Bytes("\2")},
       Bytes("\2,a,\2,b")},

      {"array_reverse", {Null(Int64ArrayType())}, Null(Int64ArrayType())},
      {"array_reverse", {Int64Array({})}, Int64Array({})},
      {"array_reverse", {Int64Array({1})}, Int64Array({1})},
      {"array_reverse",
       {Int64Array({1, 2, 3, 4, 5, 6, 7, 8, 9, 10})},
       Int64Array({10, 9, 8, 7, 6, 5, 4, 3, 2, 1})},
      {"array_reverse",
       {StringArray({"", "foo", "bar", "baz", "foobar"})},
       StringArray({"foobar", "baz", "bar", "foo", ""})},
      {"array_reverse",
       {DoubleArray({3.14, 1.59, -0.0, double_pos_inf, double_neg_inf})},
       DoubleArray({double_neg_inf, double_pos_inf, -0.0, 1.59, 3.14})},
      {"array_reverse",
       {Value::Array(array_struct_type, {struct0, struct1, struct2})},
       Value::Array(array_struct_type, {struct2, struct1, struct0})},

      {"array_is_distinct", {Null(Int64ArrayType())}, NullBool()},
      {"array_is_distinct", {Int64Array({1, 2, 3})}, True()},
      {"array_is_distinct", {Int64Array({1, 1, 1})}, False()},
      {"array_is_distinct", {Array({Int64(1), Int64(2), NullInt64()})}, True()},
      {"array_is_distinct",
       {Array({Int64(1), Int64(1), NullInt64()})},
       False()},
      {"array_is_distinct",
       {Array({Int64(1), NullInt64(), NullInt64()})},
       False()},
      {"array_is_distinct", {StringArray({"foo", "foo"})}, False()},
      {"array_is_distinct",
       {Array({String(""), String("foo"), NullString()})},
       True()},
      {"array_is_distinct", {DoubleArray({0.0, 1.0})}, True()},
      {"array_is_distinct", {DoubleArray({0.0, 0.0})}, False()},
      {"array_is_distinct", {DoubleArray({0.0, double_nan})}, True()},
      {"array_is_distinct", {DoubleArray({double_nan, double_nan})}, False()},
  };
  std::vector<FunctionTestCall> tests_array_concat = GetArrayConcatTests();
  results.insert(results.end(),
                 std::make_move_iterator(tests_array_concat.begin()),
                 std::make_move_iterator(tests_array_concat.end())
  );
  return results;
}

std::vector<QueryParamsWithResult> GetFunctionTestsArrayConcatOperator() {
  std::vector<QueryParamsWithResult> results;
  // array || array -> array
  // Test the test cases of array_concat function.
  for (const FunctionTestCall& function_call : GetArrayConcatTests()) {
    if (function_call.params.params().size() == 2) {
      results.push_back(function_call.params);
    }
  }
  return results;
}

std::vector<FunctionTestCall> GetFunctionTestsBase32() {
  const std::vector<std::pair<Value, Value>> unencoded_and_encoded = {
      {NullBytes(), NullString()},
      {Bytes(""), String("")},
      {Bytes("1"), String("GE======")},
      {Bytes("1a"), String("GFQQ====")},
      {Bytes("12345"), String("GEZDGNBV")},
      {Bytes("123456"), String("GEZDGNBVGY======")},
      {Bytes("1234567890123456"),
       String("GEZDGNBVGY3TQOJQGEZDGNBVGY======")},
  };
  std::vector<FunctionTestCall> tests;
  for (const auto& entry : unencoded_and_encoded) {
    tests.push_back({"to_base32", {entry.first}, entry.second});
    tests.push_back({"from_base32", {entry.second}, entry.first});
  }
  tests.push_back({"from_base32", {String("GE0")}, NullBytes(), OUT_OF_RANGE});
  tests.push_back({"from_base32", {String("12")}, NullBytes(), OUT_OF_RANGE});
  return tests;
}

std::vector<FunctionTestCall> GetFunctionTestsBase64() {
  const std::vector<std::pair<Value, Value>> unencoded_and_encoded = {
      {NullBytes(), NullString()},
      {Bytes(""), String("")},
      {Bytes(" "), String("IA==")},
      {Bytes("abcABC"), String("YWJjQUJD")},
      {Bytes("abcABCжщфЖЩФ"), String("YWJjQUJD0LbRidGE0JbQqdCk")},
      {Bytes("Ḋ"), String("4biK")},
      {Bytes("abca\0b\0c\0"), String("YWJjYQBiAGMA")},
  };
  std::vector<FunctionTestCall> tests;
  for (const auto& entry : unencoded_and_encoded) {
    tests.push_back({"to_base64", {entry.first}, entry.second});
    tests.push_back({"from_base64", {entry.second}, entry.first});
  }
  tests.push_back({"from_base64", {String("====")}, NullBytes(), OUT_OF_RANGE});
  return tests;
}

std::vector<FunctionTestCall> GetFunctionTestsHex() {
  const std::vector<std::pair<Value, Value>> unencoded_and_encoded = {
      {NullBytes(), NullString()},
      {Bytes(""), String("")},
      {Bytes(" "), String("20")},
      {Bytes("abcABC"), String("616263414243")},
      {Bytes("abcABCжщфЖЩФ"), String("616263414243d0b6d189d184d096d0a9d0a4")},
      {Bytes("\0\0a\ff\xee"), String("0000610c66ee")},
      {Bytes("\x01\x23\x45\x67\x89\xAB\xCD\xEF\x55\xAA"),
       String("0123456789abcdef55aa")},
      {Bytes("\xFE\xDC\xBA\x98\x76\x54\x32\x10\xAA\x55"),
       String("fedcba9876543210aa55")},
  };
  std::vector<FunctionTestCall> tests;
  for (const auto& entry : unencoded_and_encoded) {
    tests.push_back({"to_hex", {entry.first}, entry.second});
    tests.push_back({"from_hex", {entry.second}, entry.first});
  }
  tests.push_back({"from_hex", {String("G")}, NullBytes(), OUT_OF_RANGE});
  tests.push_back({"from_hex", {String("123g321")}, NullBytes(), OUT_OF_RANGE});
  tests.push_back(
      {"from_hex", {String("abcde00011-")}, NullBytes(), OUT_OF_RANGE});
  tests.push_back(
      {"from_hex", {String("abcABCжщфЖЩФ")}, NullBytes(), OUT_OF_RANGE});
  return tests;
}

std::vector<FunctionTestCall> GetFunctionTestsCodePoints() {
  const Value null_array = Null(types::Int64ArrayType());

  // Contains all ASCII characters with values in the range [0, 255].
  Value all_ascii_characters;
  // Elements are numbers from 0 to 255.
  Value all_ascii_characters_array;
  {
    std::string all_ascii_characters_string(255, 'a');
    std::iota(all_ascii_characters_string.begin(),
              all_ascii_characters_string.end(),
              std::numeric_limits<uint8_t>::lowest());
    all_ascii_characters = Bytes(all_ascii_characters_string);

    std::vector<int64_t> all_ascii_characters_vector(255);
    std::iota(all_ascii_characters_vector.begin(),
              all_ascii_characters_vector.end(), 0);
    all_ascii_characters_array =
        values::Int64Array(all_ascii_characters_vector);
  }

  const std::vector<std::pair<Value, Value>> value_and_codepoints = {
      {NullBytes(), null_array},
      {NullString(), null_array},
      {Bytes(""), values::Int64Array({})},
      {String(""), values::Int64Array({})},
      {Bytes(" "), values::Int64Array({32})},
      {String(" "), values::Int64Array({32})},
      {Bytes("abcABC"), values::Int64Array({97, 98, 99, 65, 66, 67})},
      {String("abcABC"), values::Int64Array({97, 98, 99, 65, 66, 67})},
      {String("abcABCжщфЖЩФ"),
       values::Int64Array(
           {97, 98, 99, 65, 66, 67, 1078, 1097, 1092, 1046, 1065, 1060})},
      {String("Ḋ"), values::Int64Array({7690})},
      {Bytes("abca\0\1cde"),
       values::Int64Array({97, 98, 99, 97, 0, 1, 99, 100, 101})},
      // Note that the null character is not interchange valid, but can be part
      // of structurally valid UTF8 strings.
      {String("abca\0\1cde"),
       values::Int64Array({97, 98, 99, 97, 0, 1, 99, 100, 101})},
      {String("абвгд"), values::Int64Array({1072, 1073, 1074, 1075, 1076})},
      // Borders of valid codepoint ranges, which are [0, 0xD800) and
      // [0xE000, 0x10FFFF]. 0 is tested above.
      {String("\uD7FE"
              "\uD7FF"),
       values::Int64Array({0xD7FE, 0xD7FF})},
      {String("\uE000"
              "\uE001"
              "\U0010FFFE"
              "\U0010FFFF"),
       values::Int64Array({0xE000, 0xE001, 0x10FFFE, 0x10FFFF})},
      {all_ascii_characters, all_ascii_characters_array},
  };
  std::vector<FunctionTestCall> tests;
  for (const auto& entry : value_and_codepoints) {
    tests.push_back({"to_code_points", {entry.first}, entry.second});
    if (entry.first.type()->IsBytes()) {
      tests.push_back({"code_points_to_bytes", {entry.second}, entry.first});
    } else {
      tests.push_back({"code_points_to_string", {entry.second}, entry.first});
    }
  }

  // Error cases.
  // The C++ compiler rejects Unicode literals in strings that aren't valid
  // codepoints, so we have to construct them "manually".
  {
    std::string invalid_codepoint_string;
    // The first character is a valid codepoint.
    invalid_codepoint_string.push_back(0xD7);
    invalid_codepoint_string.push_back('\xFF');
    // The second is not.
    invalid_codepoint_string.push_back(0xD8);
    invalid_codepoint_string.push_back('\xFF');

    tests.push_back({"to_code_points",
                     {String(invalid_codepoint_string)},
                     null_array,
                     OUT_OF_RANGE});
  }

  {
    std::string invalid_codepoint_string;
    // The first character is a valid codepoint.
    invalid_codepoint_string.push_back(0xE0);
    invalid_codepoint_string.push_back(0x00);
    // The second is not.
    invalid_codepoint_string.push_back(0xDF);
    invalid_codepoint_string.push_back('\xFF');

    tests.push_back({"to_code_points",
                     {String(invalid_codepoint_string)},
                     null_array,
                     OUT_OF_RANGE});
  }

  {
    std::string invalid_codepoint_string;
    // Invalid three-byte codepoint (above the valid range).
    invalid_codepoint_string.push_back(0x11);
    invalid_codepoint_string.push_back('\xFF');
    invalid_codepoint_string.push_back('\xFF');

    tests.push_back({"to_code_points",
                     {String(invalid_codepoint_string)},
                     null_array,
                     OUT_OF_RANGE});
  }

  // Invalid ASCII values.
  tests.push_back({"code_points_to_bytes",
                   {values::Int64Array({0, -1})},
                   values::NullBytes(),
                   OUT_OF_RANGE});
  tests.push_back({"code_points_to_bytes",
                   {values::Int64Array({256, 10})},
                   values::NullBytes(),
                   OUT_OF_RANGE});
  tests.push_back({"code_points_to_bytes",
                   {values::Int64Array({0, 1, 512})},
                   values::NullBytes(),
                   OUT_OF_RANGE});

  // Invalid codepoints.
  tests.push_back({"code_points_to_string",
                   {values::Int64Array({0, -1})},
                   values::NullString(),
                   OUT_OF_RANGE});
  tests.push_back({"code_points_to_string",
                   {values::Int64Array({-100})},
                   values::NullString(),
                   OUT_OF_RANGE});
  tests.push_back({"code_points_to_string",
                   {values::Int64Array({0, 1, 0xD800})},
                   values::NullString(),
                   OUT_OF_RANGE});
  tests.push_back({"code_points_to_string",
                   {values::Int64Array({10, 100, 0x10FFFF + 1})},
                   values::NullString(),
                   OUT_OF_RANGE});
  tests.push_back({"code_points_to_string",
                   {values::Int64Array({std::numeric_limits<int64_t>::max()})},
                   values::NullString(),
                   OUT_OF_RANGE});

  return tests;
}

std::vector<FunctionTestCall> GetFunctionTestsPadding() {
  std::vector<FunctionTestCall> tests = {
      {"lpad_bytes",
       {Bytes("google"), Int64(-8), Bytes("")},
       NullBytes(),
       OUT_OF_RANGE},
      {"lpad_bytes",
       {Bytes("goog"), Int64(10), Bytes("")},
       NullBytes(),
       OUT_OF_RANGE},
      {"lpad_bytes", {Bytes("abcdef"), Int64(0), Bytes("defgh")}, ""},
      {"lpad_bytes", {Bytes("abcdef"), Int64(6), Bytes("defgh")}, "abcdef"},
      {"lpad_bytes", {Bytes("abcdef"), Int64(4), Bytes("defgh")}, "abcd"},
      {"lpad_bytes", {Bytes("abcdef"), Int64(3), Bytes("defgh")}, "abc"},
      {"lpad_bytes", {Bytes("abcde"), Int64(4), Bytes("defgh")}, "abcd"},
      {"lpad_bytes", {Bytes("abcde"), Int64(3), Bytes("defgh")}, "abc"},
      {"lpad_bytes", {Bytes("abc"), Int64(5), Bytes("defgh")}, "deabc"},
      {"lpad_bytes", {Bytes("abc"), Int64(7), Bytes("defg")}, "defgabc"},
      {"lpad_bytes", {Bytes("abc"), Int64(4), Bytes("def")}, "dabc"},
      {"lpad_bytes", {Bytes("abc"), Int64(10), Bytes("defg")}, "defgdefabc"},
      {"lpad_bytes", {Bytes("abcx"), Int64(5), Bytes("defgh")}, "dabcx"},
      {"lpad_bytes", {Bytes("abcx"), Int64(7), Bytes("defg")}, "defabcx"},
      {"lpad_bytes", {Bytes("abcx"), Int64(10), Bytes("defg")}, "defgdeabcx"},
      {"lpad_bytes", {Bytes("abc"), Int64(7), Bytes("-")}, "----abc"},
      {"lpad_bytes", {Bytes(""), Int64(7), Bytes("-")}, "-------"},
      {"lpad_bytes", {Bytes(""), Int64(7), Bytes("def")}, "defdefd"},

      {"lpad_bytes", {Bytes("abc"), Int64(7)}, "    abc"},
      {"lpad_bytes", {Bytes(""), Int64(7)}, "       "},
      {"lpad_bytes", {Bytes("abcx"), Int64(10)}, "      abcx"},

      {"rpad_bytes",
       {Bytes("google"), Int64(-8), Bytes("")},
       NullBytes(),
       OUT_OF_RANGE},
      {"rpad_bytes",
       {Bytes("goog"), Int64(10), Bytes("")},
       NullBytes(),
       OUT_OF_RANGE},
      {"rpad_bytes", {Bytes("abcdef"), Int64(0), Bytes("defgh")}, ""},
      {"rpad_bytes", {Bytes("abcdef"), Int64(6), Bytes("defgh")}, "abcdef"},
      {"rpad_bytes", {Bytes("abcdef"), Int64(4), Bytes("defgh")}, "abcd"},
      {"rpad_bytes", {Bytes("abcdef"), Int64(3), Bytes("defgh")}, "abc"},
      {"rpad_bytes", {Bytes("abc"), Int64(5), Bytes("defgh")}, "abcde"},
      {"rpad_bytes", {Bytes("abc"), Int64(7), Bytes("defg")}, "abcdefg"},
      {"rpad_bytes", {Bytes("abc"), Int64(4), Bytes("def")}, "abcd"},
      {"rpad_bytes", {Bytes("abc"), Int64(10), Bytes("defg")}, "abcdefgdef"},
      {"rpad_bytes", {Bytes("abcx"), Int64(5), Bytes("defgh")}, "abcxd"},
      {"rpad_bytes", {Bytes("abcx"), Int64(7), Bytes("defg")}, "abcxdef"},
      {"rpad_bytes", {Bytes("abcx"), Int64(8), Bytes("def")}, "abcxdefd"},
      {"rpad_bytes", {Bytes("abcx"), Int64(10), Bytes("defg")}, "abcxdefgde"},
      {"rpad_bytes", {Bytes("abc"), Int64(7), Bytes("-")}, "abc----"},
      {"rpad_bytes", {Bytes(""), Int64(7), Bytes("-")}, "-------"},
      {"rpad_bytes", {Bytes(""), Int64(7), Bytes("def")}, "defdefd"},

      {"rpad_bytes", {Bytes("abc"), Int64(7)}, "abc    "},
      {"rpad_bytes", {Bytes(""), Int64(7)}, "       "},
      {"rpad_bytes", {Bytes("abcx"), Int64(10)}, "abcx      "},

      {"rpad_string",
       {"者不終朝ef而", Int64(-8), ""},
       NullString(),
       OUT_OF_RANGE},
      {"rpad_string",
       {"者不終朝ef而", Int64(10), ""},
       NullString(),
       OUT_OF_RANGE},
      {"rpad_string", {"abcdef", Int64(0), "defgh"}, ""},
      {"rpad_string", {"abcdef", Int64(6), "defgh"}, "abcdef"},
      {"rpad_string", {"abcdef", Int64(4), "defgh"}, "abcd"},
      {"rpad_string", {"abcdef", Int64(3), "defgh"}, "abc"},
      {"rpad_string", {"abc", Int64(5), "defgh"}, "abcde"},
      {"rpad_string", {"abc", Int64(7), "defg"}, "abcdefg"},
      {"rpad_string", {"abc", Int64(4), "def"}, "abcd"},
      {"rpad_string", {"abc", Int64(10), "defg"}, "abcdefgdef"},
      {"rpad_string", {"abcx", Int64(5), "defgh"}, "abcxd"},
      {"rpad_string", {"abcx", Int64(7), "defg"}, "abcxdef"},
      {"rpad_string", {"abcx", Int64(8), "def"}, "abcxdefd"},
      {"rpad_string", {"abcx", Int64(10), "defg"}, "abcxdefgde"},
      {"rpad_string", {"abc", Int64(7), "-"}, "abc----"},
      {"rpad_string", {"", Int64(7), "-"}, "-------"},
      {"rpad_string", {"", Int64(7), "def"}, "defdefd"},
      {"rpad_string", {"¼¼¼𠈓𠈓𠈓", Int64(6), "智者不終朝"}, "¼¼¼𠈓𠈓𠈓"},
      {"rpad_string", {"¼¼¼𠈓𠈓𠈓", Int64(5), "智者不終朝"}, "¼¼¼𠈓𠈓"},
      {"rpad_string", {"¼¼¼𠈓𠈓𠈓", Int64(7), "智者不終朝"}, "¼¼¼𠈓𠈓𠈓智"},
      {"rpad_string", {"¼¼¼𠈓𠈓𠈓", Int64(10), "智者朝"}, "¼¼¼𠈓𠈓𠈓智者朝智"},

      {"rpad_string", {"¼¼¼𠈓𠈓𠈓", Int64(7)}, "¼¼¼𠈓𠈓𠈓 "},
      {"rpad_string", {"¼¼¼𠈓𠈓𠈓", Int64(10)}, "¼¼¼𠈓𠈓𠈓    "},
      {"rpad_string", {"", Int64(5)}, "     "},

      {"lpad_string", {"google", Int64(-8), ""}, NullString(), OUT_OF_RANGE},
      {"lpad_string", {"goog", Int64(10), ""}, NullString(), OUT_OF_RANGE},
      {"lpad_string", {"abcdef", Int64(0), "defgh"}, ""},
      {"lpad_string", {"abcdef", Int64(6), "defgh"}, "abcdef"},
      {"lpad_string", {"abcdef", Int64(4), "defgh"}, "abcd"},
      {"lpad_string", {"abcdef", Int64(3), "defgh"}, "abc"},
      {"lpad_string", {"abcde", Int64(4), "defgh"}, "abcd"},
      {"lpad_string", {"abcde", Int64(3), "defgh"}, "abc"},
      {"lpad_string", {"abc", Int64(5), "defgh"}, "deabc"},
      {"lpad_string", {"abc", Int64(7), "defg"}, "defgabc"},
      {"lpad_string", {"abc", Int64(4), "def"}, "dabc"},
      {"lpad_string", {"abc", Int64(10), "defg"}, "defgdefabc"},
      {"lpad_string", {"abcx", Int64(5), "defgh"}, "dabcx"},
      {"lpad_string", {"abcx", Int64(7), "defg"}, "defabcx"},
      {"lpad_string", {"abcx", Int64(10), "defg"}, "defgdeabcx"},
      {"lpad_string", {"abc", Int64(7), "-"}, "----abc"},
      {"lpad_string", {"", Int64(7), "-"}, "-------"},
      {"lpad_string", {"", Int64(7), "def"}, "defdefd"},
      {"lpad_string", {"¼¼¼𠈓𠈓𠈓", Int64(6), "智者不終朝"}, "¼¼¼𠈓𠈓𠈓"},
      {"lpad_string", {"¼¼¼𠈓𠈓𠈓", Int64(5), "智者不終朝"}, "¼¼¼𠈓𠈓"},
      {"lpad_string", {"¼¼¼𠈓𠈓𠈓", Int64(7), "智者不終朝"}, "智¼¼¼𠈓𠈓𠈓"},
      {"lpad_string", {"¼¼¼𠈓𠈓𠈓", Int64(10), "智者朝"}, "智者朝智¼¼¼𠈓𠈓𠈓"},
      {"lpad_string", {"¼¼𠈓𠈓", Int64(10), "智者朝"}, "智者朝智者朝¼¼𠈓𠈓"},

      {"lpad_string", {"¼¼¼𠈓𠈓𠈓", Int64(7)}, " ¼¼¼𠈓𠈓𠈓"},
      {"lpad_string", {"¼¼¼𠈓𠈓𠈓", Int64(10)}, "    ¼¼¼𠈓𠈓𠈓"},
      {"lpad_string", {"", Int64(5)}, "     "},
  };

  {
    std::string bad_utf8;

    bad_utf8.push_back(0xD7);
    bad_utf8.push_back('\xFF');
    bad_utf8.push_back(0xD8);
    bad_utf8.push_back('\xFF');

    tests.push_back({"lpad_string",
                     {"valid", Int64(10), bad_utf8},
                     NullString(),
                     OUT_OF_RANGE});

    tests.push_back({"lpad_string",
                     {bad_utf8, Int64(10), "valid"},
                     NullString(),
                     OUT_OF_RANGE});

    tests.push_back({"rpad_string",
                     {"valid", Int64(10), bad_utf8},
                     NullString(),
                     OUT_OF_RANGE});

    tests.push_back({"rpad_string",
                     {bad_utf8, Int64(10), "valid"},
                     NullString(),
                     OUT_OF_RANGE});
  }
  return tests;
}

std::vector<FunctionTestCall> GetFunctionTestsRepeat() {
  return {
      {"repeat", {Bytes("google"), Int64(-8)}, NullString(), OUT_OF_RANGE},
      {"repeat", {Bytes("abcx"), Int64(0)}, ""},
      {"repeat", {Bytes("abcx"), Int64(1)}, "abcx"},
      {"repeat", {Bytes("abcx"), Int64(2)}, "abcxabcx"},
      {"repeat", {Bytes("abcx"), Int64(3)}, "abcxabcxabcx"},
      {"repeat", {Bytes("abc"), Int64(2)}, "abcabc"},
      {"repeat", {Bytes(""), Int64(3)}, ""},
      {"repeat", {Bytes(""), Int64(2147483648)}, ""},
      {"repeat", {"", Int64(100)}, ""},
      {"repeat", {"¼¼¼𠈓𠈓𠈓", Int64(1)}, "¼¼¼𠈓𠈓𠈓"},
      {"repeat", {"¼¼¼a", Int64(2)}, "¼¼¼a¼¼¼a"},
      {"repeat", {"¼¼¼a", Int64(3)}, "¼¼¼a¼¼¼a¼¼¼a"},
      {"repeat", {"¼¼¼a", Int64((1 << 28))}, NullString(), OUT_OF_RANGE},
      {"repeat",
       {"¼¼¼a", Int64(std::numeric_limits<int64_t>::max())},
       NullString(),
       OUT_OF_RANGE},
  };
}

std::vector<FunctionTestCall> GetFunctionTestsReverse() {
  return {
      // String inputs.
      {"reverse", {NullString()}, NullString()},
      {"reverse", {String("")}, String("")},
      {"reverse", {String("abcABC")}, String("CBAcba")},
      {"reverse", {String("abcABCжщфЖЩФ¼𠈓")}, String("𠈓¼ФЩЖфщжCBAcba")},
      // Byte inputs.
      {"reverse", {NullBytes()}, NullBytes()},
      {"reverse", {Bytes("")}, Bytes("")},
      {"reverse", {Bytes("abcABC")}, Bytes("CBAcba")},
      {"reverse", {Bytes("\0\0\1\2\3\4")}, Bytes("\4\3\2\1\0\0")},
  };
}

std::vector<FunctionTestCall> GetFunctionTestsFromProto() {
  return {
      // Proto3 Timestamp Conversions
      {"from_proto", {Proto3Timestamp(1234, 5000)}, Timestamp(1234000005)},
      {"from_proto",
       {Proto3Timestamp(1391258096, 123450000)},
       Timestamp(1391258096123450)},
      {"from_proto",
       {Proto3Timestamp(962409600, 123456000)},
       Timestamp(962409600123456)},
      {"from_proto",
       {Proto3Timestamp(1391258096, 0)},
       Timestamp(1391258096000000)},
      {"from_proto", {Proto3Timestamp(-50400, 1000)}, Timestamp(-50399999999)},
      {"from_proto",
       {Proto3Timestamp(1391258096, 123450000)},
       Timestamp(1391258096123450)},
      {"from_proto", {Proto3Timestamp(50400, 1000)}, Timestamp(50400000001)},
      {"from_proto",
       {Proto3Timestamp(1232496059, 0)},
       Timestamp(1232496059000000)},
      {"from_proto",
       {Proto3Timestamp(253402300799, 999999999)},
       Timestamp(types::kTimestampMax)},
      {"from_proto",
       {Proto3Timestamp(-62135596800, 0)},
       Timestamp(types::kTimestampMin)},
      {"from_proto", {Proto3Timestamp(123, -1)}, NullTimestamp(), OUT_OF_RANGE},
      {"from_proto",
       {Proto3Timestamp(0, 1000000000)},
       NullTimestamp(),
       OUT_OF_RANGE},
      {"from_proto",
       {Proto3Timestamp(-62135596825, 0)},
       NullTimestamp(),
       OUT_OF_RANGE},

      // Proto3 Date Conversions
      {"from_proto", {Proto3Date(9999, 12, 31)}, DateFromStr("9999-12-31")},
      {"from_proto", {Proto3Date(0001, 1, 1)}, DateFromStr("0001-01-01")},
      {"from_proto", {Proto3Date(1960, 1, 07)}, DateFromStr("1960-01-07")},
      {"from_proto", {Proto3Date(1999, 12, 31)}, DateFromStr("1999-12-31")},
      {"from_proto", {Proto3Date(500, 8, 30)}, DateFromStr("0500-08-30")},
      {"from_proto", {Proto3Date(2016, 2, 29)}, DateFromStr("2016-02-29")},
      {"from_proto", {Proto3Date(2017, 2, 29)}, NullDate(), OUT_OF_RANGE},
      {"from_proto", {Proto3Date(10000, 9, 17)}, NullDate(), OUT_OF_RANGE},
      {"from_proto", {Proto3Date(2017, 11, 31)}, NullDate(), OUT_OF_RANGE},
      {"from_proto", {Proto3Date(2014, 13, 15)}, NullDate(), OUT_OF_RANGE},
      {"from_proto", {Proto3Date(0, 1, 1)}, NullDate(), OUT_OF_RANGE},
      {"from_proto", {Proto3Date(1, 0, 1)}, NullDate(), OUT_OF_RANGE},
      {"from_proto", {Proto3Date(1, 1, 0)}, NullDate(), OUT_OF_RANGE},

      // Proto3 Wrapper Conversions
      {"from_proto",
       {Proto3Wrapper<google::protobuf::Int32Value>(-100)},
       Int32(-100)},
      {"from_proto",
       {Proto3Wrapper<google::protobuf::Int32Value>(0)},
       Int32(0)},
      {"from_proto",
       {Proto3Wrapper<google::protobuf::Int32Value>(500)},
       Int32(500)},
      {"from_proto",
       {Proto3Wrapper<google::protobuf::Int32Value>(int32max)},
       Int32(int32max)},
      {"from_proto",
       {Proto3Wrapper<google::protobuf::Int32Value>(int32min)},
       Int32(int32min)},

      {"from_proto",
       {Proto3Wrapper<google::protobuf::Int64Value>(-1000000)},
       Int64(-1000000)},
      {"from_proto",
       {Proto3Wrapper<google::protobuf::Int64Value>(0)},
       Int64(0)},
      {"from_proto",
       {Proto3Wrapper<google::protobuf::Int64Value>(200000000)},
       Int64(200000000)},
      {"from_proto",
       {Proto3Wrapper<google::protobuf::Int64Value>(int64max)},
       Int64(int64max)},

      {"from_proto",
       {Proto3Wrapper<google::protobuf::UInt32Value>(1000000)},
       Uint32(1000000)},
      {"from_proto",
       {Proto3Wrapper<google::protobuf::UInt32Value>(0)},
       Uint32(0)},
      {"from_proto",
       {Proto3Wrapper<google::protobuf::UInt32Value>(1000000000)},
       Uint32(1000000000)},
      {"from_proto",
       {Proto3Wrapper<google::protobuf::UInt32Value>(uint32max)},
       Uint32(uint32max)},

      {"from_proto",
       {Proto3Wrapper<google::protobuf::UInt64Value>(10000000)},
       Uint64(10000000)},
      {"from_proto",
       {Proto3Wrapper<google::protobuf::UInt64Value>(0)},
       Uint64(0)},
      {"from_proto",
       {Proto3Wrapper<google::protobuf::UInt64Value>(3000000000)},
       Uint64(3000000000)},
      {"from_proto",
       {Proto3Wrapper<google::protobuf::UInt64Value>(uint64max)},
       Uint64(uint64max)},

      {"from_proto",
       {Proto3Wrapper<google::protobuf::DoubleValue>(-12345467.89111)},
       Double(-12345467.89111)},
      {"from_proto",
       {Proto3Wrapper<google::protobuf::DoubleValue>(0)},
       Double(0)},
      {"from_proto",
       {Proto3Wrapper<google::protobuf::DoubleValue>(9876543210.123456)},
       Double(9876543210.123456)},
      {"from_proto",
       {Proto3Wrapper<google::protobuf::DoubleValue>(doublemax)},
       Double(doublemax)},
      {"from_proto",
       {Proto3Wrapper<google::protobuf::DoubleValue>(doublemin)},
       Double(doublemin)},
      {"from_proto",
       {Proto3Wrapper<google::protobuf::DoubleValue>(double_nan)},
       Double(double_nan)},

      {"from_proto",
       {Proto3Wrapper<google::protobuf::FloatValue>(-1234.891f)},
       Float(-1234.891f)},
      {"from_proto",
       {Proto3Wrapper<google::protobuf::FloatValue>(0)},
       Float(0)},
      {"from_proto",
       {Proto3Wrapper<google::protobuf::FloatValue>(9910.12f)},
       Float(9910.12f)},
      {"from_proto",
       {Proto3Wrapper<google::protobuf::FloatValue>(floatmin)},
       Float(floatmin)},
      {"from_proto",
       {Proto3Wrapper<google::protobuf::FloatValue>(floatmax)},
       Float(floatmax)},
      {"from_proto",
       {Proto3Wrapper<google::protobuf::FloatValue>(float_nan)},
       Float(float_nan)},

      {"from_proto",
       {Proto3Wrapper<google::protobuf::StringValue>("abcdefg")},
       String("abcdefg")},
      {"from_proto",
       {Proto3Wrapper<google::protobuf::StringValue>("")},
       String("")},
      {"from_proto",
       {Proto3Wrapper<google::protobuf::StringValue>("?!#$%^&*!")},
       String("?!#$%^&*!")},
      {"from_proto",
       {Proto3Wrapper<google::protobuf::StringValue>("   ыфabcщ   ")},
       String("   ыфabcщ   ")},
      {"from_proto",
       {Proto3Wrapper<google::protobuf::StringValue>("12356789")},
       String("12356789")},
      {"from_proto",
       {Proto3Wrapper<google::protobuf::BytesValue>(absl::Cord("abcdefg"))},
       Bytes(absl::Cord("abcdefg"))},
      {"from_proto",
       {Proto3Wrapper<google::protobuf::BytesValue>(absl::Cord(""))},
       Bytes(absl::Cord(""))},
      {"from_proto",
       {Proto3Wrapper<google::protobuf::BytesValue>(absl::Cord("\xd7\x00"))},
       Bytes("\xd7")},
      {"from_proto",
       {Proto3Wrapper<google::protobuf::BytesValue>(
           absl::Cord("\xFF\x80\x81xyz\x81\x80\xFF"))},
       Bytes("\xFF\x80\x81xyz\x81\x80\xFF")},
      {"from_proto",
       {Proto3Wrapper<google::protobuf::BytesValue>(absl::Cord("a \0xyza \0"))},
       Bytes("a ")},
      {"from_proto",
       {Proto3Wrapper<google::protobuf::BoolValue>(true)},
       Bool(true)},
      {"from_proto",
       {Proto3Wrapper<google::protobuf::BoolValue>(false)},
       Bool(false)},

      // Idempotent tests
      {"from_proto", {Bool(false)}, Bool(false)},
      {"from_proto", {Bytes("abc")}, Bytes("abc")},
      {"from_proto", {String("abc")}, String("abc")},
      {"from_proto", {Int32(123)}, Int32(123)},
      {"from_proto", {Uint32(123)}, Uint32(123)},
      {"from_proto", {Int64(123)}, Int64(123)},
      {"from_proto", {Uint64(123)}, Uint64(123)},
      {"from_proto", {Double(12.3)}, Double(12.3)},
      {"from_proto", {Float(9910.12f)}, Float(9910.12f)},
      {"from_proto", {DateFromStr("1960-01-07")}, DateFromStr("1960-01-07")},
      {"from_proto", {Timestamp(1234000005)}, Timestamp(1234000005)},
      {"from_proto", {Bool(false)}, Bool(false)},

      // Null input
      {"from_proto", {NullInt64()}, NullInt64()},
      {"from_proto", {Value::Null(Proto3DateType())}, NullDate()},
      {"from_proto", {Value::Null(Proto3TimestampType())}, NullTimestamp()},
  };
}

static absl::Status FunctionEvalError() {
  return absl::Status(absl::StatusCode::kOutOfRange,
                      "Function Evaluation Error");
}

std::vector<QueryParamsWithResult>  GetFunctionTestsFromProto3TimeOfDay() {
  std::vector<CivilTimeTestCase> test_cases = {
      {{Proto3TimeOfDay(0, 0, 0, 0)}, TimeFromStr("00:00:00")},
      {{{Proto3TimeOfDay(12, 34, 56, 123456)}},
       TimeFromStr("12:34:56.000123"),
       TimeFromStr("12:34:56.000123456", functions::kNanoseconds)},
      {{{Proto3TimeOfDay(23, 59, 59, 999999)}},
       TimeFromStr("23:59:59.000999"),
       TimeFromStr("23:59:59.000999999", functions::kNanoseconds)},
      {{{Proto3TimeOfDay(7, 0, 6, 654321)}},
       TimeFromStr("07:00:06.000654"),
       TimeFromStr("07:00:06.000654321", functions::kNanoseconds)},
      {{{Proto3TimeOfDay(13, 30, 17, 987654)}},
       TimeFromStr("13:30:17.000987"),
       TimeFromStr("13:30:17.000987654", functions::kNanoseconds)},
      {{TimeFromStr("12:34:56.000987")}, TimeFromStr("12:34:56.000987")},
      {{TimeFromStr("12:34:56.000987654", functions::kNanoseconds)},
       TimeFromStr("12:34:56.000987654", functions::kNanoseconds)},
      {{Proto3TimeOfDay(02, 0, 59, 0)}, TimeFromStr("02:00:59")},
      {{Proto3TimeOfDay(02, 0, 60, 0)}, FunctionEvalError(), TimeType()},
      {{Proto3TimeOfDay(02, 0, 30, 1000000000)},
       FunctionEvalError(),
       TimeType()},
      {{Proto3TimeOfDay(24, 0, 0, 0)}, FunctionEvalError(), TimeType()}};

  std::vector<QueryParamsWithResult> result;
  result.reserve(test_cases.size());
  for (const CivilTimeTestCase& test : test_cases) {
    AddTestCaseWithWrappedResultForCivilTimeAndNanos(test, &result);
  }

  return result;
}


// To generate the TO_PROTO tests, we take the FROM_PROTO tests and swap
// the argument and result values.  For tests where the FROM_PROTO argument
// is a proto (the normal case), we also add an idempotent TO_PROTO test.
std::vector<FunctionTestCall> GetFunctionTestsToProto() {
  const std::string kFunctionName = "to_proto";
  std::vector<FunctionTestCall> test_cases;
  test_cases.reserve(GetFunctionTestsFromProto().size());
  for (const FunctionTestCall& test : GetFunctionTestsFromProto()) {
    if (!test.params.status().ok() || !test.params.param(0).type()->IsProto() ||
        test.params.result() == Timestamp(types::kTimestampMax)) {
      // Skip FROM_PROTO tests where the argument is not a proto (FROM_PROTO
      // idempotent tests), and tests that return errors.  Also skip tests
      // where the FROM_PROTO result is kTimestampMax (micros precision) as it
      // not possible for TO_PROTO to produce the max Proto3 Timestamp (nanos
      // precision) value from a micros TIMESTAMP.
      continue;
    }
    test_cases.emplace_back(
        kFunctionName, ValueConstructor::FromValues({test.params.param(0)}),
        test.params.param(0));
    test_cases.emplace_back(
        kFunctionName, ValueConstructor::FromValues({test.params.result()}),
        test.params.param(0));
  }

  // Null input
  test_cases.push_back(
      {"to_proto",
       {NullInt64()},
       Null(Proto3Wrapper<google::protobuf::Int64Value>(0).type())});

  return test_cases;
}

std::vector<QueryParamsWithResult> GetFunctionTestsToProto3TimeOfDay() {
  std::vector<QueryParamsWithResult> test_cases = {
      {{TimeFromStr("00:00:00")}, Proto3TimeOfDay(0, 0, 0, 0)},
      {{TimeFromStr("12:34:56.000123")}, Proto3TimeOfDay(12, 34, 56, 123000)},
      {{TimeFromStr("23:59:59.000999")}, Proto3TimeOfDay(23, 59, 59, 999000)},
      {{TimeFromStr("07:00:06.000654")}, Proto3TimeOfDay(7, 0, 6, 654000)},
      {{TimeFromStr("13:30:17.000987")}, Proto3TimeOfDay(13, 30, 17, 987000)},
      {{TimeFromStr("02:00:59")}, Proto3TimeOfDay(02, 0, 59, 0)},
      {{Proto3TimeOfDay(13, 30, 17, 987000)},
       Proto3TimeOfDay(13, 30, 17, 987000)},
      // The signature from PROTO to PROTO is a no-op, so nanos will flow
      // through even when the TIMESTAMP_NANOS feature is not enabled.
      {{Proto3TimeOfDay(13, 30, 17, 123456789)},
       Proto3TimeOfDay(13, 30, 17, 123456789)}};

  for (auto&& test : test_cases) {
    test = test.WrapWithFeature(FEATURE_CIVIL_TIME);
  }

  std::vector<QueryParamsWithResult> nanos_test_cases = {
      {{TimeFromStr("12:34:56.000123456", functions::kNanoseconds)},
       Proto3TimeOfDay(12, 34, 56, 123456)},
      {{TimeFromStr("23:59:59.000999999", functions::kNanoseconds)},
       Proto3TimeOfDay(23, 59, 59, 999999)},
      {{TimeFromStr("07:00:06.000654321", functions::kNanoseconds)},
       Proto3TimeOfDay(7, 0, 6, 654321)},
      {{TimeFromStr("13:30:17.000987654", functions::kNanoseconds)},
       Proto3TimeOfDay(13, 30, 17, 987654)},
      {{TimeFromStr("13:30:17.000987654", functions::kNanoseconds)},
       Proto3TimeOfDay(13, 30, 17, 987654)},
      {{TimeFromStr("13:30:17.123456789", functions::kNanoseconds)},
       Proto3TimeOfDay(13, 30, 17, 123456789)}};

  for (auto&& test : nanos_test_cases) {
    test =
        test.WrapWithFeatureSet({FEATURE_CIVIL_TIME, FEATURE_TIMESTAMP_NANOS});
    test_cases.push_back(test);
  }
  return test_cases;
}

std::vector<FunctionTestCall> GetFunctionTestsBytesStringConversion() {
  const absl::btree_map<std::string, std::vector<std::pair<Value, Value>>>
      format_bytes_string = {
          {"hex",
           {
               {Bytes("abcABC"), String("616263414243")},
               {Bytes("abcABCжщфЖЩФ"),
                String("616263414243d0b6d189d184d096d0a9d0a4")},
               {Bytes("\0\0a\ff\xee"), String("0000610c66ee")},
               {Bytes("\x01\x23\x45\x67\x89\xAB\xCD\xEF\x55\xAA"),
                String("0123456789abcdef55aa")},
               {Bytes("\xFE\xDC\xBA\x98\x76\x54\x32\x10\xAA\x55"),
                String("fedcba9876543210aa55")},
           }},
          {"base64",
           {
               {Bytes(" "), String("IA==")},
               {Bytes("abcABC"), String("YWJjQUJD")},
               {Bytes("abcABCжщфЖЩФ"), String("YWJjQUJD0LbRidGE0JbQqdCk")},
               {Bytes("Ḋ"), String("4biK")},
               {Bytes("abca\0b\0c\0"), String("YWJjYQBiAGMA")},
               {Bytes("\x5a\x1b\x5a\x5a\x1b\x5a\x5a\x1b\x5a\x5a\x1b\x5a\x5a\x1b"
                      "\x5a\x5a\x1b\x5a\x5a\x1b\x5a\x5a\x1b\x5a\x5a\x1b\x5a\x5a"
                      "\x1b\x5a\x5a\x1b\x5a\x5a\x1b\x5a\x5a\x1b\x5a\x5a\x1b\x5a"
                      "\x5a\x1b\x5a\x5a\x1b\x5a\x5a\x1b\x5a\x5a\x1b\x5a\x5a\x1b"
                      "\x5a\x5a\x1b\x5a"),
                String("WhtaWhtaWhtaWhtaWhtaWhtaWhtaWhtaWhtaWhtaWhtaWhtaWhtaWht"
                       "aWhtaWhtaWhtaWhtaWhtaWhta")},
           }},
          {"base64m",
           {
               {Bytes(" "), String("IA==")},
               {Bytes("abcABC"), String("YWJjQUJD")},
               {Bytes("abcABCжщфЖЩФ"), String("YWJjQUJD0LbRidGE0JbQqdCk")},
               {Bytes("Ḋ"), String("4biK")},
               {Bytes("abca\0b\0c\0"), String("YWJjYQBiAGMA")},
               {Bytes("\x5a\x1b\x5a\x5a\x1b\x5a\x5a\x1b\x5a\x5a\x1b\x5a\x5a\x1b"
                      "\x5a\x5a\x1b\x5a\x5a\x1b\x5a\x5a\x1b\x5a\x5a\x1b\x5a\x5a"
                      "\x1b\x5a\x5a\x1b\x5a\x5a\x1b\x5a\x5a\x1b\x5a\x5a\x1b\x5a"
                      "\x5a\x1b\x5a\x5a\x1b\x5a\x5a\x1b\x5a\x5a\x1b\x5a\x5a\x1b"
                      "\x5a\x5a\x1b\x5a"),
                String("WhtaWhtaWhtaWhtaWhtaWhtaWhtaWhtaWhtaWhtaWhtaWhtaWhtaWht"
                       "aWhtaWhtaWhtaWhtaWhta\nWhta")},
               {Bytes("\x5a\x1b\x5a\x5a\x1b\x5a\x5a\x1b\x5a\x5a\x1b\x5a\x5a\x1b"
                      "\x5a\x5a\x1b\x5a\x5a\x1b\x5a\x5a\x1b\x5a\x5a\x1b\x5a\x5a"
                      "\x1b\x5a\x5a\x1b\x5a\x5a\x1b\x5a\x5a\x1b\x5a\x5a\x1b\x5a"
                      "\x5a\x1b\x5a\x5a\x1b\x5a\x5a\x1b\x5a\x5a\x1b\x5a\x5a\x1b"
                      "\x5a\x5a\x1b\x5a\x5a\x1b\x5a\x5a\x1b\x5a\x5a\x1b\x5a\x5a"
                      "\x1b\x5a\x5a\x1b\x5a\x5a\x1b\x5a\x5a\x1b\x5a\x5a\x1b\x5a"
                      "\x5a\x1b\x5a\x5a\x1b\x5a\x5a\x1b\x5a\x5a\x1b\x5a\x5a\x1b"
                      "\x5a\x5a\x1b\x5a\x5a\x1b\x5a\x5a\x1b\x5a\x5a\x1b\x5a\x5a"
                      "\x1b\x5a\x5a\x1b\x5a"),
                String("WhtaWhtaWhtaWhtaWhtaWhtaWhtaWhtaWhtaWhtaWhtaWhtaWhtaWht"
                       "aWhtaWhtaWhtaWhtaWhta\nWhtaWhtaWhtaWhtaWhtaWhtaWhtaWhta"
                       "WhtaWhtaWhtaWhtaWhtaWhtaWhtaWhtaWhtaWhtaWhta\nWhta")},
               {Bytes("\x5a\x1b\x5a\x5a\x1b\x5a\x5a\x1b\x5a\x5a\x1b\x5a\x5a\x1b"
                      "\x5a\x5a\x1b\x5a\x5a\x1b\x5a\x5a\x1b\x5a\x5a\x1b\x5a\x5a"
                      "\x1b\x5a\x5a\x1b\x5a\x5a\x1b\x5a\x5a\x1b\x5a\x5a\x1b\x5a"
                      "\x5a\x1b\x5a\x5a\x1b\x5a\x5a\x1b\x5a\x5a\x1b\x5a\x5a\x1b"
                      "\x5a\x5a\x1b\x5a\x5a\x1b\x5a\x5a\x1b\x5a\x5a\x1b\x5a\x5a"
                      "\x1b\x5a\x5a\x1b\x5a\x5a\x1b\x5a\x5a\x1b\x5a\x5a\x1b\x5a"
                      "\x5a\x1b\x5a\x5a\x1b\x5a\x5a\x1b\x5a\x5a\x1b\x5a\x5a\x1b"
                      "\x5a\x5a\x1b\x5a\x5a\x1b\x5a\x5a\x1b\x5a\x5a\x1b\x5a\x5a"
                      "\x1b\x5a"),
                String("WhtaWhtaWhtaWhtaWhtaWhtaWhtaWhtaWhtaWhtaWhtaWhtaWhtaWht"
                       "aWhtaWhtaWhtaWhtaWhta\nWhtaWhtaWhtaWhtaWhtaWhtaWhtaWhta"
                       "WhtaWhtaWhtaWhtaWhtaWhtaWhtaWhtaWhtaWhtaWhta")},
           }},
          {"base2",
           {
               {Bytes("\x6A"), String("01101010")},
               {Bytes("\x17\x6A"), String("0001011101101010")},
           }},
          {"base8",
           {
               {Bytes("\x3F"), String("077")},
               {Bytes("\x11\x3B"), String("010473")},
               {Bytes("\x02\x11\x3B"), String("00410473")},
               {Bytes("\x2C\x26\x02\x11\x3B"), String("02604600410473")},
           }},
          {"base32",
           {
               {Bytes("1"), String("GE======")},
               {Bytes("1a"), String("GFQQ====")},
               {Bytes("12345"), String("GEZDGNBV")},
               {Bytes("123456"), String("GEZDGNBVGY======")},
               {Bytes("1234567890123456"),
                String("GEZDGNBVGY3TQOJQGEZDGNBVGY======")},
           }},
          {"ascii",
           {
               {Bytes("\x61"), String("a")},
               {Bytes("\x61\x62\x63"), String("abc")},
           }},
          {"utf8",
           {
               {Bytes("\x24"), String("$")},
               {Bytes("\xC2\xA3"), String("£")},
               {Bytes("\xE2\x82\xAC"), String("€")},
               {Bytes("\xE2\x82\xAC\xC2\xA3"), String("€£")},
           }},
      };

  const std::vector<std::pair<Value, Value>> common_string_bytes = {
      {NullBytes(), NullString()},
      {Bytes(""), String("")},
  };

  std::vector<FunctionTestCall> tests;
  const std::vector<std::string> supported_formats = {
      "base2",  "base8",   "base16", "hex",
      "base64", "base64m", "ascii",  "utf-8", "utf8"};
  for (const std::string& supported_format : supported_formats) {
    std::string format_key = supported_format;
    if (format_key == "base16")
      format_key = "hex";
    else if (format_key == "utf-8")
      format_key = "utf8";

    for (const auto& entry : common_string_bytes) {
      tests.push_back(
          {"bytes_to_string", {entry.first, supported_format}, entry.second});
      tests.push_back(
          {"string_to_bytes", {entry.second, supported_format}, entry.first});
    }
    for (const auto& entry : format_bytes_string.at(format_key)) {
      tests.push_back(
          {"bytes_to_string", {entry.first, supported_format}, entry.second});
      tests.push_back(
          {"string_to_bytes", {entry.second, supported_format}, entry.first});
    }
  }

  // Null or unsupported format
  tests.push_back(
      {"bytes_to_string", {Bytes("\x17\x6A"), NullString()}, NullString()});
  tests.push_back({"string_to_bytes",
                   {String("0001011101101010"), NullString()},
                   NullBytes()});

  // Not encodable input bytes for specific encodings
  tests.push_back({"bytes_to_string",
                   {Bytes("\xff"), "ascii"},
                   NullString(),
                   OUT_OF_RANGE});
  tests.push_back({"bytes_to_string",
                   {Bytes("\x61\x61\xff\x61"), "ascii"},
                   NullString(),
                   OUT_OF_RANGE});
  tests.push_back({"bytes_to_string",
                   {Bytes("\xff"), "utf-8"},
                   NullString(),
                   OUT_OF_RANGE});
  tests.push_back({"bytes_to_string",
                   {Bytes("\xe2\x82\xac\xff"), "utf-8"},
                   NullString(),
                   OUT_OF_RANGE});

  // irregular number of digits to unescape
  tests.push_back(
      {"string_to_bytes", {String("1101010"), "base2"}, Bytes("\x6A")});
  tests.push_back({"string_to_bytes",
                   {String("1011101101010"), "base2"},
                   Bytes("\x17\x6A")});
  tests.push_back({"string_to_bytes",
                   {String("01011101101010"), "base2"},
                   Bytes("\x17\x6A")});
  tests.push_back({"string_to_bytes",
                   {String("001011101101010"), "base2"},
                   Bytes("\x17\x6A")});

  tests.push_back({"string_to_bytes", {String("77"), "base8"}, Bytes("\x3F")});
  tests.push_back(
      {"string_to_bytes", {String("10473"), "base8"}, Bytes("\x11\x3B")});
  tests.push_back(
      {"string_to_bytes", {String("0410473"), "base8"}, Bytes("\x02\x11\x3B")});
  tests.push_back({"string_to_bytes",
                   {String("2604600410473"), "base8"},
                   Bytes("\x2C\x26\x02\x11\x3B")});

  // Not decodable input string for specific encodings
  tests.push_back(
      {"string_to_bytes", {String("012"), "base2"}, NullBytes(), OUT_OF_RANGE});
  tests.push_back(
      {"string_to_bytes", {String("099"), "base8"}, NullBytes(), OUT_OF_RANGE});
  tests.push_back({"string_to_bytes",
                   {String("123g321"), "base16"},
                   NullBytes(),
                   OUT_OF_RANGE});
  tests.push_back({"string_to_bytes",
                   {String("123g321"), "hex"},
                   NullBytes(),
                   OUT_OF_RANGE});
  tests.push_back({"string_to_bytes",
                   {String("3!2+7w=="), "base64"},
                   NullBytes(),
                   OUT_OF_RANGE});
  tests.push_back({"string_to_bytes",
                   {String("3!2+7w=="), "base64m"},
                   NullBytes(),
                   OUT_OF_RANGE});
  tests.push_back({"string_to_bytes",
                   {String("sd£sd"), "ascii"},
                   NullBytes(),
                   OUT_OF_RANGE});
  tests.push_back({"string_to_bytes",
                   {String("sd\xffsd"), "ascii"},
                   NullBytes(),
                   OUT_OF_RANGE});
  tests.push_back({"string_to_bytes",
                   {String("£\xff£"), "utf8"},
                   NullBytes(),
                   OUT_OF_RANGE});
  tests.push_back({"string_to_bytes",
                   {String("£\xff£"), "utf-8"},
                   NullBytes(),
                   OUT_OF_RANGE});

  return tests;
}

}  // namespace zetasql
